--- /dev/null
+CS4270 audio CODEC
+
+The driver for this device currently only supports I2C.
+
+Required properties:
+
+ - compatible : "cirrus,cs4270"
+
+ - reg : the I2C address of the device for I2C
+
+Optional properties:
+
+ - reset-gpio : a GPIO spec for the reset pin. If specified, it will be
+ deasserted before communication to the codec starts.
+
+Example:
+
+codec: cs4270@48 {
+ compatible = "cirrus,cs4270";
+ reg = <0x48>;
+};
--- /dev/null
+Cirrus Logic CS4271 DT bindings
+
+This driver supports both the I2C and the SPI bus.
+
+Required properties:
+
+ - compatible: "cirrus,cs4271"
+
+For required properties on SPI, please consult
+Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Required properties on I2C:
+
+ - reg: the i2c address
+
+
+Optional properties:
+
+ - reset-gpio: a GPIO spec to define which pin is connected to the chip's
+ !RESET pin
+
+Examples:
+
+ codec_i2c: cs4271@10 {
+ compatible = "cirrus,cs4271";
+ reg = <0x10>;
+ reset-gpio = <&gpio 23 0>;
+ };
+
+ codec_spi: cs4271@0 {
+ compatible = "cirrus,cs4271";
+ reg = <0x0>;
+ reset-gpio = <&gpio 23 0>;
+ spi-max-frequency = <6000000>;
+ };
+
--- /dev/null
+Texas Instruments McASP controller
+
+Required properties:
+- compatible :
+ "ti,dm646x-mcasp-audio" : for DM646x platforms
+ "ti,da830-mcasp-audio" : for both DA830 & DA850 platforms
+ "ti,omap2-mcasp-audio" : for OMAP2 platforms (TI81xx, AM33xx)
+
+- reg : Should contain McASP registers offset and length
+- interrupts : Interrupt number for McASP
+- op-mode : I2S/DIT ops mode.
+- tdm-slots : Slots for TDM operation.
+- num-serializer : Serializers used by McASP.
+- serial-dir : A list of serializer pin mode. The list number should be equal
+ to "num-serializer" parameter. Each entry is a number indication
+ serializer pin direction. (0 - INACTIVE, 1 - TX, 2 - RX)
+
+
+Optional properties:
+
+- ti,hwmods : Must be "mcasp<n>", n is controller instance starting 0
+- tx-num-evt : FIFO levels.
+- rx-num-evt : FIFO levels.
+- sram-size-playback : size of sram to be allocated during playback
+- sram-size-capture : size of sram to be allocated during capture
+
+Example:
+
+mcasp0: mcasp0@1d00000 {
+ compatible = "ti,da830-mcasp-audio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x100000 0x3000>;
+ interrupts = <82 83>;
+ op-mode = <0>; /* MCASP_IIS_MODE */
+ tdm-slots = <2>;
+ num-serializer = <16>;
+ serial-dir = <
+ 0 0 0 0 /* 0: INACTIVE, 1: TX, 2: RX */
+ 0 0 0 0
+ 0 0 0 1
+ 2 0 0 0 >;
+ tx-num-evt = <1>;
+ rx-num-evt = <1>;
+};
--- /dev/null
+* Texas Instruments OMAP4+ and twl6040 based audio setups
+
+Required properties:
+- compatible: "ti,abe-twl6040"
+- ti,model: Name of the sound card ( for example "SDP4430")
+- ti,mclk-freq: MCLK frequency for HPPLL operation
+- ti,mcpdm: phandle for the McPDM node
+- ti,twl6040: phandle for the twl6040 core node
+- ti,audio-routing: List of connections between audio components.
+ Each entry is a pair of strings, the first being the connection's sink,
+ the second being the connection's source.
+
+Optional properties:
+- ti,dmic: phandle for the OMAP dmic node if the machine have it connected
+- ti,jack_detection: Need to be set to <1> if the board capable to detect jack
+ insertion, removal.
+
+Available audio endpoints for the audio-routing table:
+
+Board connectors:
+ * Headset Stereophone
+ * Earphone Spk
+ * Ext Spk
+ * Line Out
+ * Vibrator
+ * Headset Mic
+ * Main Handset Mic
+ * Sub Handset Mic
+ * Line In
+ * Digital Mic
+
+twl6040 pins:
+ * HSOL
+ * HSOR
+ * EP
+ * HFL
+ * HFR
+ * AUXL
+ * AUXR
+ * VIBRAL
+ * VIBRAR
+ * HSMIC
+ * MAINMIC
+ * SUBMIC
+ * AFML
+ * AFMR
+
+ * Headset Mic Bias
+ * Main Mic Bias
+ * Digital Mic1 Bias
+ * Digital Mic2 Bias
+
+Digital mic pins:
+ * DMic
+
+Example:
+
+sound {
+ compatible = "ti,abe-twl6040";
+ ti,model = "SDP4430";
+
+ ti,jack-detection = <1>;
+ ti,mclk-freq = <38400000>;
+
+ ti,mcpdm = <&mcpdm>;
+ ti,dmic = <&dmic>;
+
+ ti,twl6040 = <&twl6040>;
+
+ /* Audio routing */
+ ti,audio-routing =
+ "Headset Stereophone", "HSOL",
+ "Headset Stereophone", "HSOR",
+ "Earphone Spk", "EP",
+ "Ext Spk", "HFL",
+ "Ext Spk", "HFR",
+ "Line Out", "AUXL",
+ "Line Out", "AUXR",
+ "Vibrator", "VIBRAL",
+ "Vibrator", "VIBRAR",
+ "HSMIC", "Headset Mic",
+ "Headset Mic", "Headset Mic Bias",
+ "MAINMIC", "Main Handset Mic",
+ "Main Handset Mic", "Main Mic Bias",
+ "SUBMIC", "Sub Handset Mic",
+ "Sub Handset Mic", "Main Mic Bias",
+ "AFML", "Line In",
+ "AFMR", "Line In",
+ "DMic", "Digital Mic",
+ "Digital Mic", "Digital Mic1 Bias";
+};
--- /dev/null
+* Texas Instruments OMAP2+ McBSP module
+
+Required properties:
+- compatible: "ti,omap2420-mcbsp" for McBSP on OMAP2420
+ "ti,omap2430-mcbsp" for McBSP on OMAP2430
+ "ti,omap3-mcbsp" for McBSP on OMAP3
+ "ti,omap4-mcbsp" for McBSP on OMAP4 and newer SoC
+- reg: Register location and size, for OMAP4+ as an array:
+ <MPU access base address, size>,
+ <L3 interconnect address, size>;
+- reg-names: Array of strings associated with the address space
+- interrupts: Interrupt numbers for the McBSP port, as an array in case the
+ McBSP IP have more interrupt lines:
+ <OCP compliant irq>,
+ <TX irq>,
+ <RX irq>;
+- interrupt-names: Array of strings associated with the interrupt numbers
+- interrupt-parent: The parent interrupt controller
+- ti,buffer-size: Size of the FIFO on the port (OMAP2430 and newer SoC)
+- ti,hwmods: Name of the hwmod associated to the McBSP port
+
+Example:
+
+mcbsp2: mcbsp@49022000 {
+ compatible = "ti,omap3-mcbsp";
+ reg = <0x49022000 0xff>,
+ <0x49028000 0xff>;
+ reg-names = "mpu", "sidetone";
+ interrupts = <0 17 0x4>, /* OCP compliant interrupt */
+ <0 62 0x4>, /* TX interrupt */
+ <0 63 0x4>, /* RX interrupt */
+ <0 4 0x4>; /* Sidetone */
+ interrupt-names = "common", "tx", "rx", "sidetone";
+ interrupt-parent = <&intc>;
+ ti,buffer-size = <1280>;
+ ti,hwmods = "mcbsp2";
+};
--- /dev/null
+* Texas Instruments SoC with twl4030 based audio setups
+
+Required properties:
+- compatible: "ti,omap-twl4030"
+- ti,model: Name of the sound card (for example "omap3beagle")
+- ti,mcbsp: phandle for the McBSP node
+- ti,codec: phandle for the twl4030 audio node
+
+Example:
+
+sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "omap3beagle";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+};
--- /dev/null
+Texas Instruments - tlv320aic3x Codec module
+
+The tlv320aic3x serial control bus communicates through I2C protocols
+
+Required properties:
+- compatible - "string" - "ti,tlv320aic3x"
+- reg - <int> - I2C slave address
+
+
+Optional properties:
+
+- gpio-reset - gpio pin number used for codec reset
+- ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
+
+Example:
+
+tlv320aic3x: tlv320aic3x@1b {
+ compatible = "ti,tlv320aic3x";
+ reg = <0x1b>;
+};
[Multiple options for each card instance]
model - force the model name
- position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF,
- 3 = VIACOMBO, 4 = COMBO)
+ position_fix - Fix DMA pointer
+ -1 = system default: choose appropriate one per controller
+ hardware
+ 0 = auto: falls back to LPIB when POSBUF doesn't work
+ 1 = use LPIB
+ 2 = POSBUF: use position buffer
+ 3 = VIACOMBO: VIA-specific workaround for capture
+ 4 = COMBO: use LPIB for playback, auto for capture stream
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
When the bit 8 (0x100) is set, the lower 8 bits are used
as the "fixed" codec slots; i.e. the driver probes the
--- /dev/null
+ALSA PCM channel-mapping API
+============================
+ Takashi Iwai <tiwai@suse.de>
+
+GENERAL
+-------
+
+The channel mapping API allows user to query the possible channel maps
+and the current channel map, also optionally to modify the channel map
+of the current stream.
+
+A channel map is an array of position for each PCM channel.
+Typically, a stereo PCM stream has a channel map of
+ { front_left, front_right }
+while a 4.0 surround PCM stream has a channel map of
+ { front left, front right, rear left, rear right }.
+
+The problem, so far, was that we had no standard channel map
+explicitly, and applications had no way to know which channel
+corresponds to which (speaker) position. Thus, applications applied
+wrong channels for 5.1 outputs, and you hear suddenly strange sound
+from rear. Or, some devices secretly assume that center/LFE is the
+third/fourth channels while others that C/LFE as 5th/6th channels.
+
+Also, some devices such as HDMI are configurable for different speaker
+positions even with the same number of total channels. However, there
+was no way to specify this because of lack of channel map
+specification. These are the main motivations for the new channel
+mapping API.
+
+
+DESIGN
+------
+
+Actually, "the channel mapping API" doesn't introduce anything new in
+the kernel/user-space ABI perspective. It uses only the existing
+control element features.
+
+As a ground design, each PCM substream may contain a control element
+providing the channel mapping information and configuration. This
+element is specified by:
+ iface = SNDRV_CTL_ELEM_IFACE_PCM
+ name = "Playback Channel Map" or "Capture Channel Map"
+ device = the same device number for the assigned PCM substream
+ index = the same index number for the assigned PCM substream
+
+Note the name is different depending on the PCM substream direction.
+
+Each control element provides at least the TLV read operation and the
+read operation. Optionally, the write operation can be provided to
+allow user to change the channel map dynamically.
+
+* TLV
+
+The TLV operation gives the list of available channel
+maps. A list item of a channel map is usually a TLV of
+ type data-bytes ch0 ch1 ch2...
+where type is the TLV type value, the second argument is the total
+bytes (not the numbers) of channel values, and the rest are the
+position value for each channel.
+
+As a TLV type, either SNDRV_CTL_TLVT_CHMAP_FIXED,
+SNDRV_CTL_TLV_CHMAP_VAR or SNDRV_CTL_TLVT_CHMAP_PAIRED can be used.
+The _FIXED type is for a channel map with the fixed channel position
+while the latter two are for flexible channel positions. _VAR type is
+for a channel map where all channels are freely swappable and _PAIRED
+type is where pair-wise channels are swappable. For example, when you
+have {FL/FR/RL/RR} channel map, _PAIRED type would allow you to swap
+only {RL/RR/FL/FR} while _VAR type would allow even swapping FL and
+RR.
+
+These new TLV types are defined in sound/tlv.h.
+
+The available channel position values are defined in sound/asound.h,
+here is a cut:
+
+/* channel positions */
+enum {
+ SNDRV_CHMAP_UNKNOWN = 0,
+ SNDRV_CHMAP_NA, /* N/A, silent */
+ SNDRV_CHMAP_MONO, /* mono stream */
+ /* this follows the alsa-lib mixer channel value + 3 */
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_RL, /* rear left */
+ SNDRV_CHMAP_RR, /* rear right */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_RC, /* rear center */
+ /* new definitions */
+ SNDRV_CHMAP_FLC, /* front left center */
+ SNDRV_CHMAP_FRC, /* front right center */
+ SNDRV_CHMAP_RLC, /* rear left center */
+ SNDRV_CHMAP_RRC, /* rear right center */
+ SNDRV_CHMAP_FLW, /* front left wide */
+ SNDRV_CHMAP_FRW, /* front right wide */
+ SNDRV_CHMAP_FLH, /* front left high */
+ SNDRV_CHMAP_FCH, /* front center high */
+ SNDRV_CHMAP_FRH, /* front right high */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_TFL, /* top front left */
+ SNDRV_CHMAP_TFR, /* top front right */
+ SNDRV_CHMAP_TFC, /* top front center */
+ SNDRV_CHMAP_TRL, /* top rear left */
+ SNDRV_CHMAP_TRR, /* top rear right */
+ SNDRV_CHMAP_TRC, /* top rear center */
+ SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC,
+};
+
+When a PCM stream can provide more than one channel map, you can
+provide multiple channel maps in a TLV container type. The TLV data
+to be returned will contain such as:
+ SNDRV_CTL_TLVT_CONTAINER 96
+ SNDRV_CTL_TLVT_CHMAP_FIXED 4 SNDRV_CHMAP_FC
+ SNDRV_CTL_TLVT_CHMAP_FIXED 8 SNDRV_CHMAP_FL SNDRV_CHMAP_FR
+ SNDRV_CTL_TLVT_CHMAP_FIXED 16 NDRV_CHMAP_FL SNDRV_CHMAP_FR \
+ SNDRV_CHMAP_RL SNDRV_CHMAP_RR
+
+The channel position is provided in LSB 16bits. The upper bits are
+used for bit flags.
+
+#define SNDRV_CHMAP_POSITION_MASK 0xffff
+#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
+#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
+
+SNDRV_CHMAP_PHASE_INVERSE indicates the channel is phase inverted,
+(thus summing left and right channels would result in almost silence).
+Some digital mic devices have this.
+
+When SNDRV_CHMAP_DRIVER_SPEC is set, all the channel position values
+don't follow the standard definition above but driver-specific.
+
+* READ OPERATION
+
+The control read operation is for providing the current channel map of
+the given stream. The control element returns an integer array
+containing the position of each channel.
+
+When this is performed before the number of the channel is specified
+(i.e. hw_params is set), it should return all channels set to
+UNKNOWN.
+
+* WRITE OPERATION
+
+The control write operation is optional, and only for devices that can
+change the channel configuration on the fly, such as HDMI. User needs
+to pass an integer value containing the valid channel positions for
+all channels of the assigned PCM substream.
+
+This operation is allowed only at PCM PREPARED state. When called in
+other states, it shall return an error.
AD1882 / AD1882A
================
- 3stack 3-stack mode (default)
+ 3stack 3-stack mode
+ 3stack-automute 3-stack with automute front HP (default)
6stack 6-stack mode
AD1884A / AD1883 / AD1984A / AD1984B
--- /dev/null
+/*
+ * TI DaVinci Audio definitions
+ */
+#ifndef __ASM_ARCH_DAVINCI_ASP_H
+#define __ASM_ARCH_DAVINCI_ASP_H
+
+/* Bases of dm644x and dm355 register banks */
+#define DAVINCI_ASP0_BASE 0x01E02000
+#define DAVINCI_ASP1_BASE 0x01E04000
+
+/* Bases of dm365 register banks */
+#define DAVINCI_DM365_ASP0_BASE 0x01D02000
+
+/* Bases of dm646x register banks */
+#define DAVINCI_DM646X_MCASP0_REG_BASE 0x01D01000
+#define DAVINCI_DM646X_MCASP1_REG_BASE 0x01D01800
+
+/* Bases of da850/da830 McASP0 register banks */
+#define DAVINCI_DA8XX_MCASP0_REG_BASE 0x01D00000
+
+/* Bases of da830 McASP1 register banks */
+#define DAVINCI_DA830_MCASP1_REG_BASE 0x01D04000
+
+/* EDMA channels of dm644x and dm355 */
+#define DAVINCI_DMA_ASP0_TX 2
+#define DAVINCI_DMA_ASP0_RX 3
+#define DAVINCI_DMA_ASP1_TX 8
+#define DAVINCI_DMA_ASP1_RX 9
+
+/* EDMA channels of dm646x */
+#define DAVINCI_DM646X_DMA_MCASP0_AXEVT0 6
+#define DAVINCI_DM646X_DMA_MCASP0_AREVT0 9
+#define DAVINCI_DM646X_DMA_MCASP1_AXEVT1 12
+
+/* EDMA channels of da850/da830 McASP0 */
+#define DAVINCI_DA8XX_DMA_MCASP0_AREVT 0
+#define DAVINCI_DA8XX_DMA_MCASP0_AXEVT 1
+
+/* EDMA channels of da830 McASP1 */
+#define DAVINCI_DA830_DMA_MCASP1_AREVT 2
+#define DAVINCI_DA830_DMA_MCASP1_AXEVT 3
+
+/* Interrupts */
+#define DAVINCI_ASP0_RX_INT IRQ_MBRINT
+#define DAVINCI_ASP0_TX_INT IRQ_MBXINT
+#define DAVINCI_ASP1_RX_INT IRQ_MBRINT
+#define DAVINCI_ASP1_TX_INT IRQ_MBXINT
+
+#endif /* __ASM_ARCH_DAVINCI_ASP_H */
#include <linux/davinci_emac.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-
-#include <mach/asp.h>
+#include <linux/platform_data/davinci_asp.h>
#include <linux/platform_data/keyscan-davinci.h>
#include <mach/hardware.h>
+#include <mach/edma.h>
#include <media/davinci/vpfe_capture.h>
#include <media/davinci/vpif_types.h>
#include <mach/cpuidle.h>
#include "clock.h"
+#include "asp.h"
#define DA8XX_TPCC_BASE 0x01c00000
#define DA8XX_TPTC0_BASE 0x01c08000
.resource = da850_mcasp_resources,
};
-static struct platform_device davinci_pcm_device = {
- .name = "davinci-pcm-audio",
- .id = -1,
-};
-
void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata)
{
- platform_device_register(&davinci_pcm_device);
-
/* DA830/OMAP-L137 has 3 instances of McASP */
if (cpu_is_davinci_da830() && id == 1) {
da830_mcasp1_device.dev.platform_data = pdata;
/*-------------------------------------------------------------------------*/
-static struct platform_device davinci_pcm_device = {
- .name = "davinci-pcm-audio",
- .id = -1,
-};
-
-static void davinci_init_pcm(void)
-{
- platform_device_register(&davinci_pcm_device);
-}
-
/*-------------------------------------------------------------------------*/
struct davinci_timer_instance davinci_timer_instance[2] = {
/* please keep these calls, and their implementations above,
* in alphabetical order so they're easier to sort through.
*/
- davinci_init_pcm();
davinci_init_wdt();
return 0;
#include <mach/time.h>
#include <mach/serial.h>
#include <mach/common.h>
-#include <mach/asp.h>
#include <linux/platform_data/spi-davinci.h>
#include <mach/gpio-davinci.h>
#include "davinci.h"
#include "clock.h"
#include "mux.h"
+#include "asp.h"
#define DM355_UART2_BASE (IO_PHYS + 0x206000)
#include <mach/time.h>
#include <mach/serial.h>
#include <mach/common.h>
-#include <mach/asp.h>
#include <linux/platform_data/keyscan-davinci.h>
#include <linux/platform_data/spi-davinci.h>
#include <mach/gpio-davinci.h>
#include "davinci.h"
#include "clock.h"
#include "mux.h"
+#include "asp.h"
#define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */
#include <mach/time.h>
#include <mach/serial.h>
#include <mach/common.h>
-#include <mach/asp.h>
#include <mach/gpio-davinci.h>
#include "davinci.h"
#include "clock.h"
#include "mux.h"
+#include "asp.h"
/*
* Device specific clocks
#include <mach/time.h>
#include <mach/serial.h>
#include <mach/common.h>
-#include <mach/asp.h>
#include <mach/gpio-davinci.h>
#include "davinci.h"
#include "clock.h"
#include "mux.h"
+#include "asp.h"
#define DAVINCI_VPIF_BASE (0x01C12000)
+++ /dev/null
-/*
- * <mach/asp.h> - DaVinci Audio Serial Port support
- */
-#ifndef __ASM_ARCH_DAVINCI_ASP_H
-#define __ASM_ARCH_DAVINCI_ASP_H
-
-#include <mach/irqs.h>
-#include <mach/edma.h>
-
-/* Bases of dm644x and dm355 register banks */
-#define DAVINCI_ASP0_BASE 0x01E02000
-#define DAVINCI_ASP1_BASE 0x01E04000
-
-/* Bases of dm365 register banks */
-#define DAVINCI_DM365_ASP0_BASE 0x01D02000
-
-/* Bases of dm646x register banks */
-#define DAVINCI_DM646X_MCASP0_REG_BASE 0x01D01000
-#define DAVINCI_DM646X_MCASP1_REG_BASE 0x01D01800
-
-/* Bases of da850/da830 McASP0 register banks */
-#define DAVINCI_DA8XX_MCASP0_REG_BASE 0x01D00000
-
-/* Bases of da830 McASP1 register banks */
-#define DAVINCI_DA830_MCASP1_REG_BASE 0x01D04000
-
-/* EDMA channels of dm644x and dm355 */
-#define DAVINCI_DMA_ASP0_TX 2
-#define DAVINCI_DMA_ASP0_RX 3
-#define DAVINCI_DMA_ASP1_TX 8
-#define DAVINCI_DMA_ASP1_RX 9
-
-/* EDMA channels of dm646x */
-#define DAVINCI_DM646X_DMA_MCASP0_AXEVT0 6
-#define DAVINCI_DM646X_DMA_MCASP0_AREVT0 9
-#define DAVINCI_DM646X_DMA_MCASP1_AXEVT1 12
-
-/* EDMA channels of da850/da830 McASP0 */
-#define DAVINCI_DA8XX_DMA_MCASP0_AREVT 0
-#define DAVINCI_DA8XX_DMA_MCASP0_AXEVT 1
-
-/* EDMA channels of da830 McASP1 */
-#define DAVINCI_DA830_DMA_MCASP1_AREVT 2
-#define DAVINCI_DA830_DMA_MCASP1_AXEVT 3
-
-/* Interrupts */
-#define DAVINCI_ASP0_RX_INT IRQ_MBRINT
-#define DAVINCI_ASP0_TX_INT IRQ_MBXINT
-#define DAVINCI_ASP1_RX_INT IRQ_MBRINT
-#define DAVINCI_ASP1_TX_INT IRQ_MBXINT
-
-struct snd_platform_data {
- u32 tx_dma_offset;
- u32 rx_dma_offset;
- enum dma_event_q asp_chan_q; /* event queue number for ASP channel */
- enum dma_event_q ram_chan_q; /* event queue number for RAM channel */
- unsigned int codec_fmt;
- /*
- * Allowing this is more efficient and eliminates left and right swaps
- * caused by underruns, but will swap the left and right channels
- * when compared to previous behavior.
- */
- unsigned enable_channel_combine:1;
- unsigned sram_size_playback;
- unsigned sram_size_capture;
-
- /*
- * If McBSP peripheral gets the clock from an external pin,
- * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR
- * and MCBSP_CLKS.
- * Depending on different hardware connections it is possible
- * to use this setting to change the behaviour of McBSP
- * driver. The dm365_clk_input_pin enum is available for dm365
- */
- int clk_input_pin;
-
- /*
- * This flag works when both clock and FS are outputs for the cpu
- * and makes clock more accurate (FS is not symmetrical and the
- * clock is very fast.
- * The clock becoming faster is named
- * i2s continuous serial clock (I2S_SCK) and it is an externally
- * visible bit clock.
- *
- * first line : WordSelect
- * second line : ContinuousSerialClock
- * third line: SerialData
- *
- * SYMMETRICAL APPROACH:
- * _______________________ LEFT
- * _| RIGHT |______________________|
- * _ _ _ _ _ _ _ _
- * _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_
- * _ _ _ _ _ _ _ _
- * _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_
- * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
- *
- * ACCURATE CLOCK APPROACH:
- * ______________ LEFT
- * _| RIGHT |_______________________________|
- * _ _ _ _ _ _ _ _ _
- * _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| |
- * _ _ _ _ dummy cycles
- * _/ \_ ... _/ \_/ \_ ... _/ \__________________
- * \_/ \_/ \_/ \_/
- *
- */
- bool i2s_accurate_sck;
-
- /* McASP specific fields */
- int tdm_slots;
- u8 op_mode;
- u8 num_serializer;
- u8 *serial_dir;
- u8 version;
- u8 txnumevt;
- u8 rxnumevt;
-};
-
-enum {
- MCASP_VERSION_1 = 0, /* DM646x */
- MCASP_VERSION_2, /* DA8xx/OMAPL1x */
-};
-
-enum dm365_clk_input_pin {
- MCBSP_CLKR = 0, /* DM365 */
- MCBSP_CLKS,
-};
-
-#define INACTIVE_MODE 0
-#define TX_MODE 1
-#define RX_MODE 2
-
-#define DAVINCI_MCASP_IIS_MODE 0
-#define DAVINCI_MCASP_DIT_MODE 1
-
-#endif /* __ASM_ARCH_DAVINCI_ASP_H */
#include <linux/platform_device.h>
#include <linux/davinci_emac.h>
#include <linux/spi/spi.h>
+#include <linux/platform_data/davinci_asp.h>
#include <mach/serial.h>
#include <mach/edma.h>
-#include <mach/asp.h>
#include <mach/pm.h>
#include <linux/platform_data/i2c-davinci.h>
#include <linux/platform_data/mmc-davinci.h>
imx27_add_imx_keypad(&eukrea_mbimx27_keymap_data);
gpio_led_register_device(-1, &eukrea_mbimx27_gpio_led_info);
+ imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);
}
platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
gpio_led_register_device(-1, &eukrea_mbimxsd_led_info);
imx_add_gpio_keys(&eukrea_mbimxsd_button_data);
+ imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);
}
platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
gpio_led_register_device(-1, &eukrea_mbimxsd_led_info);
imx_add_gpio_keys(&eukrea_mbimxsd_button_data);
+ imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);
}
gpio_led_register_device(-1, &eukrea_mbimxsd51_led_info);
imx_add_gpio_keys(&eukrea_mbimxsd51_button_data);
+ imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);
}
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/leds.h>
+#include <linux/platform_data/asoc-mx27vis.h>
#include <media/soc_camera.h>
#include <sound/tlv320aic32x4.h>
#include <asm/mach-types.h>
#define EXPBOARD_BIT1 (GPIO_PORTD + 27)
#define EXPBOARD_BIT0 (GPIO_PORTD + 28)
+#define AMP_GAIN_0 (GPIO_PORTF + 9)
+#define AMP_GAIN_1 (GPIO_PORTF + 8)
+#define AMP_MUTE_SDL (GPIO_PORTE + 5)
+#define AMP_MUTE_SDR (GPIO_PORTF + 7)
+
static const int visstrim_m10_pins[] __initconst = {
/* UART1 (console) */
PE12_PF_UART1_TXD,
EXPBOARD_BIT2 | GPIO_GPIO | GPIO_IN | GPIO_PUEN,
EXPBOARD_BIT1 | GPIO_GPIO | GPIO_IN | GPIO_PUEN,
EXPBOARD_BIT0 | GPIO_GPIO | GPIO_IN | GPIO_PUEN,
+ /* Audio AMP control */
+ AMP_GAIN_0 | GPIO_GPIO | GPIO_OUT,
+ AMP_GAIN_1 | GPIO_GPIO | GPIO_OUT,
+ AMP_MUTE_SDL | GPIO_GPIO | GPIO_OUT,
+ AMP_MUTE_SDR | GPIO_GPIO | GPIO_OUT,
};
static struct gpio visstrim_m10_version_gpios[] = {
.flags = GPIOF_DIR_OUT | GPIOF_INIT_LOW,
.label = "usbotg_cs",
},
+ {
+ .gpio = AMP_GAIN_0,
+ .flags = GPIOF_DIR_OUT,
+ .label = "amp-gain-0",
+ },
+ {
+ .gpio = AMP_GAIN_1,
+ .flags = GPIOF_DIR_OUT,
+ .label = "amp-gain-1",
+ },
+ {
+ .gpio = AMP_MUTE_SDL,
+ .flags = GPIOF_DIR_OUT,
+ .label = "amp-mute-sdl",
+ },
+ {
+ .gpio = AMP_MUTE_SDR,
+ .flags = GPIOF_DIR_OUT,
+ .label = "amp-mute-sdr",
+ },
};
/* Camera */
}
+/* Audio */
+static const struct snd_mx27vis_platform_data snd_mx27vis_pdata __initconst = {
+ .amp_gain0_gpio = AMP_GAIN_0,
+ .amp_gain1_gpio = AMP_GAIN_1,
+ .amp_mutel_gpio = AMP_MUTE_SDL,
+ .amp_muter_gpio = AMP_MUTE_SDR,
+};
+
static void __init visstrim_m10_revision(void)
{
int exp_version = 0;
imx27_add_fec(NULL);
imx_add_gpio_keys(&visstrim_gpio_keys_platform_data);
platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
- imx_add_platform_device("mx27vis", 0, NULL, 0, NULL, 0);
+ imx_add_platform_device("mx27vis", 0, NULL, 0, &snd_mx27vis_pdata,
+ sizeof(snd_mx27vis_pdata));
platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0,
&iclink_tvp5150, sizeof(iclink_tvp5150));
gpio_led_register_device(0, &visstrim_m10_led_data);
usb_musb_init(&musb_board_data);
}
+static __init void am3517_evm_mcbsp1_init(void)
+{
+ u32 devconf0;
+
+ /* McBSP1 CLKR/FSR signal to be connected to CLKX/FSX pin */
+ devconf0 = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
+ devconf0 |= OMAP2_MCBSP1_CLKR_MASK | OMAP2_MCBSP1_FSR_MASK;
+ omap_ctrl_writel(devconf0, OMAP2_CONTROL_DEVCONF0);
+}
+
static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
#if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \
/* MUSB */
am3517_evm_musb_init();
+ /* McBSP1 */
+ am3517_evm_mcbsp1_init();
+
/* MMC init function */
omap_hsmmc_init(mmc);
}
cm_t35_init_ethernet();
cm_t35_init_led();
cm_t35_init_display();
+ omap_twl4030_audio_init("cm-t3x");
usb_musb_init(NULL);
cm_t35_init_usbh();
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, devkit8000_nand_partitions,
ARRAY_SIZE(devkit8000_nand_partitions));
+ omap_twl4030_audio_init("omap3beagle");
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
igep_flash_init();
igep_leds_init();
+ omap_twl4030_audio_init("igep2");
/*
* WLAN-BT combo module from MuRata which has a Marvell WLAN
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, omap3beagle_nand_partitions,
ARRAY_SIZE(omap3beagle_nand_partitions));
+ omap_twl4030_audio_init("omap3beagle");
/* Ensure msecure is mux'd to be able to set the RTC. */
omap_mux_init_signal("sys_drm_msecure", OMAP_PIN_OFF_OUTPUT_HIGH);
omap3evm_init_smsc911x();
omap3_evm_display_init();
omap3_evm_wl12xx_init();
+ omap_twl4030_audio_init("omap3evm");
}
MACHINE_START(OMAP3EVM, "OMAP3 EVM")
overo_display_init();
overo_init_led();
overo_init_keys();
+ omap_twl4030_audio_init("overo");
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
#include "common-board-devices.h"
#define OMAP_ZOOM_WLAN_PMENA_GPIO (101)
+#define ZOOM2_HEADSET_EXTMUTE_GPIO (153)
#define OMAP_ZOOM_WLAN_IRQ_GPIO (162)
#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES)
return ret;
}
-/* EXTMUTE callback function */
-static void zoom2_set_hs_extmute(int mute)
-{
- gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute);
-}
-
static struct twl4030_gpio_platform_data zoom_gpio_data = {
.setup = zoom_twl_gpio_setup,
};
codec_data->ramp_delay_value = 3; /* 161 ms */
codec_data->hs_extmute = 1;
- codec_data->set_hs_extmute = zoom2_set_hs_extmute;
+ codec_data->hs_extmute_gpio = ZOOM2_HEADSET_EXTMUTE_GPIO;
}
omap_pmic_init(1, 2400, "twl5030", 7 + OMAP_INTC_START, &zoom_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
extern int __init zoom_debugboard_init(void);
extern void __init zoom_peripherals_init(void);
extern void __init zoom_display_init(void);
-
-#define ZOOM2_HEADSET_EXTMUTE_GPIO 153
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include <plat/omap_device.h>
#include <linux/pm_runtime.h>
-#include "control.h"
-
/*
* FIXME: Find a mechanism to enable/disable runtime the McBSP ICLK autoidle.
* Sidetone needs non-gated ICLK and sidetone autoidle is broken.
#include "cm2xxx_3xxx.h"
#include "cm-regbits-34xx.h"
-/* McBSP1 internal signal muxing function for OMAP2/3 */
-static int omap2_mcbsp1_mux_rx_clk(struct device *dev, const char *signal,
- const char *src)
-{
- u32 v;
-
- v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
-
- if (!strcmp(signal, "clkr")) {
- if (!strcmp(src, "clkr"))
- v &= ~OMAP2_MCBSP1_CLKR_MASK;
- else if (!strcmp(src, "clkx"))
- v |= OMAP2_MCBSP1_CLKR_MASK;
- else
- return -EINVAL;
- } else if (!strcmp(signal, "fsr")) {
- if (!strcmp(src, "fsr"))
- v &= ~OMAP2_MCBSP1_FSR_MASK;
- else if (!strcmp(src, "fsx"))
- v |= OMAP2_MCBSP1_FSR_MASK;
- else
- return -EINVAL;
- } else {
- return -EINVAL;
- }
-
- omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0);
-
- return 0;
-}
-
-/* McBSP4 internal signal muxing function for OMAP4 */
-#define OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_FSX (1 << 31)
-#define OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_CLKX (1 << 30)
-static int omap4_mcbsp4_mux_rx_clk(struct device *dev, const char *signal,
- const char *src)
-{
- u32 v;
-
- /*
- * In CONTROL_MCBSPLP register only bit 30 (CLKR mux), and bit 31 (FSR
- * mux) is used */
- v = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_MCBSPLP);
-
- if (!strcmp(signal, "clkr")) {
- if (!strcmp(src, "clkr"))
- v &= ~OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_CLKX;
- else if (!strcmp(src, "clkx"))
- v |= OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_CLKX;
- else
- return -EINVAL;
- } else if (!strcmp(signal, "fsr")) {
- if (!strcmp(src, "fsr"))
- v &= ~OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_FSX;
- else if (!strcmp(src, "fsx"))
- v |= OMAP4_CONTROL_MCBSPLP_ALBCTRLRX_FSX;
- else
- return -EINVAL;
- } else {
- return -EINVAL;
- }
-
- omap4_ctrl_pad_writel(v, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_MCBSPLP);
-
- return 0;
-}
-
-/* McBSP CLKS source switching function */
-static int omap2_mcbsp_set_clk_src(struct device *dev, struct clk *clk,
- const char *src)
-{
- struct clk *fck_src;
- char *fck_src_name;
- int r;
-
- if (!strcmp(src, "clks_ext"))
- fck_src_name = "pad_fck";
- else if (!strcmp(src, "clks_fclk"))
- fck_src_name = "prcm_fck";
- else
- return -EINVAL;
-
- fck_src = clk_get(dev, fck_src_name);
- if (IS_ERR_OR_NULL(fck_src)) {
- pr_err("omap-mcbsp: %s: could not clk_get() %s\n", "clks",
- fck_src_name);
- return -EINVAL;
- }
-
- pm_runtime_put_sync(dev);
-
- r = clk_set_parent(clk, fck_src);
- if (IS_ERR_VALUE(r)) {
- pr_err("omap-mcbsp: %s: could not clk_set_parent() to %s\n",
- "clks", fck_src_name);
- clk_put(fck_src);
- return -EINVAL;
- }
-
- pm_runtime_get_sync(dev);
-
- clk_put(fck_src);
-
- return 0;
-}
-
static int omap3_enable_st_clock(unsigned int id, bool enable)
{
unsigned int w;
pdata->reg_size = 4;
pdata->has_ccr = true;
}
- pdata->set_clk_src = omap2_mcbsp_set_clk_src;
-
- /* On OMAP2/3 the McBSP1 port has 6 pin configuration */
- if (id == 1 && oh->class->rev < MCBSP_CONFIG_TYPE4)
- pdata->mux_signal = omap2_mcbsp1_mux_rx_clk;
- /* On OMAP4 the McBSP4 port has 6 pin configuration */
- if (id == 4 && oh->class->rev == MCBSP_CONFIG_TYPE4)
- pdata->mux_signal = omap4_mcbsp4_mux_rx_clk;
-
- if (oh->class->rev == MCBSP_CONFIG_TYPE3) {
+ if (oh->class->rev == MCBSP_CONFIG_TYPE2) {
+ /* The FIFO has 128 locations */
+ pdata->buffer_size = 0x80;
+ } else if (oh->class->rev == MCBSP_CONFIG_TYPE3) {
if (id == 2)
/* The FIFO has 1024 + 256 locations */
pdata->buffer_size = 0x500;
static int __init omap2_mcbsp_init(void)
{
- omap_hwmod_for_each_by_class("mcbsp", omap_init_mcbsp, NULL);
+ if (!of_have_populated_dt())
+ omap_hwmod_for_each_by_class("mcbsp", omap_init_mcbsp, NULL);
return 0;
}
static struct omap_hwmod_addr_space omap44xx_mcpdm_addrs[] = {
{
+ .name = "mpu",
.pa_start = 0x40132000,
.pa_end = 0x4013207f,
.flags = ADDR_TYPE_RT
static struct omap_hwmod_addr_space omap44xx_mcpdm_dma_addrs[] = {
{
+ .name = "dma",
.pa_start = 0x49032000,
.pa_end = 0x4903207f,
.flags = ADDR_TYPE_RT
pmic_data->v2v1 = &omap4_v2v1_idata;
}
#endif /* CONFIG_ARCH_OMAP4 */
+
+#if defined(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) || \
+ defined(CONFIG_SND_OMAP_SOC_OMAP_TWL4030_MODULE)
+#include <linux/platform_data/omap-twl4030.h>
+
+static struct omap_tw4030_pdata omap_twl4030_audio_data;
+
+static struct platform_device audio_device = {
+ .name = "omap-twl4030",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap_twl4030_audio_data,
+ },
+};
+
+void __init omap_twl4030_audio_init(char *card_name)
+{
+ omap_twl4030_audio_data.card_name = card_name;
+ platform_device_register(&audio_device);
+}
+
+#else /* SOC_OMAP_TWL4030 */
+void __init omap_twl4030_audio_init(char *card_name)
+{
+ return;
+}
+#endif /* SOC_OMAP_TWL4030 */
void omap4_pmic_get_config(struct twl4030_platform_data *pmic_data,
u32 pdata_flags, u32 regulators_flags);
+void omap_twl4030_audio_init(char *card_name);
+
#endif /* __OMAP_PMIC_COMMON__ */
+++ /dev/null
-/*
- * arch/arm/mach-tegra/include/mach/tegra_wm8903_pdata.h
- *
- * Copyright 2011 NVIDIA, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-struct tegra_wm8903_platform_data {
- int gpio_spkr_en;
- int gpio_hp_det;
- int gpio_hp_mute;
- int gpio_int_mic_en;
- int gpio_ext_mic_en;
-};
break;
case DMA_CYCLIC:
desc = dmaengine_prep_dma_cyclic(chan, param->buf,
- param->len, param->period, param->direction);
+ param->len, param->period, param->direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
break;
default:
dev_err(&chan->dev->device, "unsupported format\n");
};
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
+ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+static const char * const ad1836_link[] = {
+ "bfin-tdm.0",
+ "spi0.4",
+};
+static struct platform_device bfin_ad1836_machine = {
+ .name = "bfin-snd-ad1836",
+ .id = -1,
+ .dev = {
+ .platform_data = (void *)ad1836_link,
+ },
+};
+#endif
+
static struct spi_board_info bfin_spi_board_info[] __initdata = {
#if defined(CONFIG_MTD_M25P80) \
|| defined(CONFIG_MTD_M25P80_MODULE)
#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE)
&bfin_tdm,
#endif
+
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
+ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+ &bfin_ad1836_machine,
+#endif
};
static int __init ezkit_init(void)
};
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
+ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+static const char * const ad1836_link[] = {
+ "bfin-tdm.0",
+ "spi0.4",
+};
+static struct platform_device bfin_ad1836_machine = {
+ .name = "bfin-snd-ad1836",
+ .id = -1,
+ .dev = {
+ .platform_data = (void *)ad1836_link,
+ },
+};
+#endif
+
#if defined(CONFIG_SND_BF5XX_SOC_AD73311) || \
defined(CONFIG_SND_BF5XX_SOC_AD73311_MODULE)
static const unsigned ad73311_gpio[] = {
&bfin_ac97_pcm,
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
+ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+ &bfin_ad1836_machine,
+#endif
+
#if defined(CONFIG_SND_BF5XX_SOC_AD73311) || \
defined(CONFIG_SND_BF5XX_SOC_AD73311_MODULE)
&bfin_ad73311_machine,
};
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
+ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+static const char * const ad1836_link[] = {
+ "bfin-tdm.0",
+ "spi0.4",
+};
+static struct platform_device bfin_ad1836_machine = {
+ .name = "bfin-snd-ad1836",
+ .id = -1,
+ .dev = {
+ .platform_data = (void *)ad1836_link,
+ },
+};
+#endif
+
#if defined(CONFIG_SND_BF5XX_SOC_AD73311) || \
defined(CONFIG_SND_BF5XX_SOC_AD73311_MODULE)
static const unsigned ad73311_gpio[] = {
&bfin_ac97_pcm,
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
+ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+ &bfin_ad1836_machine,
+#endif
+
#if defined(CONFIG_SND_BF5XX_SOC_AD73311) || \
defined(CONFIG_SND_BF5XX_SOC_AD73311_MODULE)
&bfin_ad73311_machine,
};
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
+ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+static const char * const ad1836_link[] = {
+ "bfin-tdm.0",
+ "spi0.4",
+};
+static struct platform_device bfin_ad1836_machine = {
+ .name = "bfin-snd-ad1836",
+ .id = -1,
+ .dev = {
+ .platform_data = (void *)ad1836_link,
+ },
+};
+#endif
+
static struct platform_device *ezkit_devices[] __initdata = {
&bfin_dpmc,
#if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE)
&bfin_ac97,
#endif
+
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
+ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+ &bfin_ad1836_machine,
+#endif
};
static int __init net2272_init(void)
};
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) \
+ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+static const char * const ad1836_link[] = {
+ "bfin-tdm.0",
+ "spi0.76",
+};
+static struct platform_device bfin_ad1836_machine = {
+ .name = "bfin-snd-ad1836",
+ .id = -1,
+ .dev = {
+ .platform_data = (void *)ad1836_link,
+ },
+};
+#endif
+
#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) || \
defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61_MODULE)
static struct platform_device adau1761_device = {
defined(CONFIG_SND_BF6XX_SOC_I2S_MODULE)
&bfin_i2s,
#endif
+#if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \
+ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE)
+ &bfin_ad1836_machine,
+#endif
#if defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) || \
defined(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61_MODULE)
&adau1761_device,
* @buf_len: total number of bytes for the entire buffer
* @period_len: number of bytes for each period
* @direction: transfer direction, to or from device
+ * @flags: tx descriptor status flags
* @context: transfer context (ignored)
*/
static struct dma_async_tx_descriptor *
atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
struct at_dma_slave *atslave = chan->private;
* @buf_len: length of the buffer (in bytes)
* @period_len: length of a single period
* @dir: direction of the operation
+ * @flags: tx descriptor status flags
* @context: operation context (ignored)
*
* Prepares a descriptor for cyclic DMA operation. This means that once the
static struct dma_async_tx_descriptor *
ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction dir, void *context)
+ enum dma_transfer_direction dir, unsigned long flags,
+ void *context)
{
struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
struct ep93xx_dma_desc *desc, *first;
static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
struct imxdma_engine *imxdma = imxdmac->imxdma;
static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
struct mmp_tdma_desc *desc;
static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
struct dma_slave_config cfg;
unsigned dma_sig;
bool cyclic;
+ bool paused;
int dma_ch;
struct omap_desc *desc;
static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction dir, void *context)
+ size_t period_len, enum dma_transfer_direction dir, unsigned long flags,
+ void *context)
{
struct omap_chan *c = to_omap_dma_chan(chan);
enum dma_slave_buswidth dev_width;
d->dev_addr = dev_addr;
d->fi = burst;
d->es = es;
- d->sync_mode = OMAP_DMA_SYNC_PACKET;
+ if (burst)
+ d->sync_mode = OMAP_DMA_SYNC_PACKET;
+ else
+ d->sync_mode = OMAP_DMA_SYNC_ELEMENT;
d->sync_type = sync_type;
d->periph_port = OMAP_DMA_PORT_MPUI;
d->sg[0].addr = buf_addr;
if (!c->cyclic) {
c->cyclic = true;
omap_dma_link_lch(c->dma_ch, c->dma_ch);
- omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+
+ if (flags & DMA_PREP_INTERRUPT)
+ omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+
omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
}
omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
}
- return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
}
static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
*/
if (c->desc) {
c->desc = NULL;
- omap_stop_dma(c->dma_ch);
+ /* Avoid stopping the dma twice */
+ if (!c->paused)
+ omap_stop_dma(c->dma_ch);
}
if (c->cyclic) {
c->cyclic = false;
+ c->paused = false;
omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
}
static int omap_dma_pause(struct omap_chan *c)
{
- /* FIXME: not supported by platform private API */
- return -EINVAL;
+ /* Pause/Resume only allowed with cyclic mode */
+ if (!c->cyclic)
+ return -EINVAL;
+
+ if (!c->paused) {
+ omap_stop_dma(c->dma_ch);
+ c->paused = true;
+ }
+
+ return 0;
}
static int omap_dma_resume(struct omap_chan *c)
{
- /* FIXME: not supported by platform private API */
- return -EINVAL;
+ /* Pause/Resume only allowed with cyclic mode */
+ if (!c->cyclic)
+ return -EINVAL;
+
+ if (c->paused) {
+ omap_start_dma(c->dma_ch);
+ c->paused = false;
+ }
+
+ return 0;
}
static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t dma_addr, size_t len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct dma_pl330_desc *desc;
struct dma_pl330_chan *pch = to_pchan(chan);
static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
- enum dma_transfer_direction dir, void *context)
+ enum dma_transfer_direction dir, unsigned long flags, void *context)
{
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_desc *txd;
static struct dma_async_tx_descriptor *
sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction direction, void *context)
+ enum dma_transfer_direction direction, unsigned long flags, void *context)
{
struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
struct sirfsoc_dma_desc *sdesc = NULL;
static struct dma_async_tx_descriptor *
dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
size_t buf_len, size_t period_len,
- enum dma_transfer_direction direction, void *context)
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
{
unsigned int periods = buf_len / period_len;
struct dma_async_tx_descriptor *txd;
struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context)
+ unsigned long flags, void *context)
{
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
struct tegra_dma_desc *dma_desc = NULL;
struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
- void *context);
+ unsigned long flags, void *context);
struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
struct dma_chan *chan, struct dma_interleaved_template *xt,
unsigned long flags);
static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction dir)
+ size_t period_len, enum dma_transfer_direction dir,
+ unsigned long flags)
{
return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
- period_len, dir, NULL);
+ period_len, dir, flags, NULL);
}
static inline int dmaengine_terminate_all(struct dma_chan *chan)
unsigned int check_defaults:1;
unsigned int reset_registers:1;
unsigned int hs_extmute:1;
- void (*set_hs_extmute)(int mute);
+ int hs_extmute_gpio;
};
struct twl4030_vibra_data {
int num_micd_rates;
struct wm8958_micd_rate *micd_rates;
+ /* Power up delays to add after microphone bias power up (ms) */
+ int micb1_delay;
+ int micb2_delay;
+
/* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1;
--- /dev/null
+#ifndef __PLATFORM_DATA_ASOC_MX27VIS_H
+#define __PLATFORM_DATA_ASOC_MX27VIS_H
+
+struct snd_mx27vis_platform_data {
+ int amp_gain0_gpio;
+ int amp_gain1_gpio;
+ int amp_mutel_gpio;
+ int amp_muter_gpio;
+};
+
+#endif /* __PLATFORM_DATA_ASOC_MX27VIS_H */
bool has_wakeup; /* Wakeup capability */
bool has_ccr; /* Transceiver has configuration control registers */
int (*enable_st_clock)(unsigned int, bool);
- int (*set_clk_src)(struct device *dev, struct clk *clk, const char *src);
- int (*mux_signal)(struct device *dev, const char *signal, const char *src);
};
/**
--- /dev/null
+/*
+ * TI DaVinci Audio Serial Port support
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DAVINCI_ASP_H
+#define __DAVINCI_ASP_H
+
+struct snd_platform_data {
+ u32 tx_dma_offset;
+ u32 rx_dma_offset;
+ int asp_chan_q; /* event queue number for ASP channel */
+ int ram_chan_q; /* event queue number for RAM channel */
+ unsigned int codec_fmt;
+ /*
+ * Allowing this is more efficient and eliminates left and right swaps
+ * caused by underruns, but will swap the left and right channels
+ * when compared to previous behavior.
+ */
+ unsigned enable_channel_combine:1;
+ unsigned sram_size_playback;
+ unsigned sram_size_capture;
+
+ /*
+ * If McBSP peripheral gets the clock from an external pin,
+ * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR
+ * and MCBSP_CLKS.
+ * Depending on different hardware connections it is possible
+ * to use this setting to change the behaviour of McBSP
+ * driver.
+ */
+ int clk_input_pin;
+
+ /*
+ * This flag works when both clock and FS are outputs for the cpu
+ * and makes clock more accurate (FS is not symmetrical and the
+ * clock is very fast.
+ * The clock becoming faster is named
+ * i2s continuous serial clock (I2S_SCK) and it is an externally
+ * visible bit clock.
+ *
+ * first line : WordSelect
+ * second line : ContinuousSerialClock
+ * third line: SerialData
+ *
+ * SYMMETRICAL APPROACH:
+ * _______________________ LEFT
+ * _| RIGHT |______________________|
+ * _ _ _ _ _ _ _ _
+ * _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_
+ * _ _ _ _ _ _ _ _
+ * _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_
+ * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
+ *
+ * ACCURATE CLOCK APPROACH:
+ * ______________ LEFT
+ * _| RIGHT |_______________________________|
+ * _ _ _ _ _ _ _ _ _
+ * _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| |
+ * _ _ _ _ dummy cycles
+ * _/ \_ ... _/ \_/ \_ ... _/ \__________________
+ * \_/ \_/ \_/ \_/
+ *
+ */
+ bool i2s_accurate_sck;
+
+ /* McASP specific fields */
+ int tdm_slots;
+ u8 op_mode;
+ u8 num_serializer;
+ u8 *serial_dir;
+ u8 version;
+ u8 txnumevt;
+ u8 rxnumevt;
+};
+
+enum {
+ MCASP_VERSION_1 = 0, /* DM646x */
+ MCASP_VERSION_2, /* DA8xx/OMAPL1x */
+ MCASP_VERSION_3, /* TI81xx/AM33xx */
+};
+
+enum mcbsp_clk_input_pin {
+ MCBSP_CLKR = 0, /* as in DM365 */
+ MCBSP_CLKS,
+};
+
+#define INACTIVE_MODE 0
+#define TX_MODE 1
+#define RX_MODE 2
+
+#define DAVINCI_MCASP_IIS_MODE 0
+#define DAVINCI_MCASP_DIT_MODE 1
+
+#endif
--- /dev/null
+/**
+ * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030
+ * codec, header.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * All rights reserved.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef _OMAP_TWL4030_H_
+#define _OMAP_TWL4030_H_
+
+struct omap_tw4030_pdata {
+ const char *card_name;
+};
+
+#endif /* _OMAP_TWL4030_H_ */
*/
struct snd_ac97;
+struct snd_pcm_chmap;
struct snd_ac97_build_ops {
int (*build_3d) (struct snd_ac97 *ac97);
struct delayed_work power_work;
#endif
struct device dev;
+
+ struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */
};
#define to_ac97_t(d) container_of(d, struct snd_ac97, dev)
unsigned int c_dma_size;
struct snd_timer *timer;
+#ifdef CONFIG_PM
+ unsigned short image[48];
+#endif
};
extern int snd_ad1816a_create(struct snd_card *card, unsigned long port,
int irq, int dma1, int dma2,
- struct snd_ad1816a **chip);
+ struct snd_ad1816a *chip);
extern int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm);
extern int snd_ad1816a_mixer(struct snd_ad1816a *chip);
extern int snd_ad1816a_timer(struct snd_ad1816a *chip, int device,
struct snd_timer **rtimer);
+#ifdef CONFIG_PM
+extern void snd_ad1816a_suspend(struct snd_ad1816a *chip);
+extern void snd_ad1816a_resume(struct snd_ad1816a *chip);
+#endif
#endif /* __SOUND_AD1816A_H */
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
};
+/* channel positions */
+enum {
+ SNDRV_CHMAP_UNKNOWN = 0,
+ SNDRV_CHMAP_NA, /* N/A, silent */
+ SNDRV_CHMAP_MONO, /* mono stream */
+ /* this follows the alsa-lib mixer channel value + 3 */
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_RL, /* rear left */
+ SNDRV_CHMAP_RR, /* rear right */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_RC, /* rear center */
+ /* new definitions */
+ SNDRV_CHMAP_FLC, /* front left center */
+ SNDRV_CHMAP_FRC, /* front right center */
+ SNDRV_CHMAP_RLC, /* rear left center */
+ SNDRV_CHMAP_RRC, /* rear right center */
+ SNDRV_CHMAP_FLW, /* front left wide */
+ SNDRV_CHMAP_FRW, /* front right wide */
+ SNDRV_CHMAP_FLH, /* front left high */
+ SNDRV_CHMAP_FCH, /* front center high */
+ SNDRV_CHMAP_FRH, /* front right high */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_TFL, /* top front left */
+ SNDRV_CHMAP_TFR, /* top front right */
+ SNDRV_CHMAP_TFC, /* top front center */
+ SNDRV_CHMAP_TRL, /* top rear left */
+ SNDRV_CHMAP_TRR, /* top rear right */
+ SNDRV_CHMAP_TRC, /* top rear center */
+ SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC,
+};
+
+#define SNDRV_CHMAP_POSITION_MASK 0xffff
+#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
+#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
+
#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int)
#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
u64 total_bytes_available;
u64 total_bytes_transferred;
wait_queue_head_t sleep;
+ void *private_data;
};
/**
#define SND_AUDIOCODEC_IEC61937 ((__u32) 0x0000000B)
#define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C)
#define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D)
+#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_G729
/*
* Profile and modes are listed with bit masks. This allows for a
--- /dev/null
+/*
+ * DA9055 ALSA Soc codec driver
+ *
+ * Copyright (c) 2012 Dialog Semiconductor
+ *
+ * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
+ * Written by David Chen <david.chen@diasemi.com> and
+ * Ashish Chavan <ashish.chavan@kpitcummins.com>
+ *
+ * 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.
+ */
+
+#ifndef __SOUND_DA9055_H__
+#define __SOUND_DA9055_H__
+
+enum da9055_micbias_voltage {
+ DA9055_MICBIAS_1_6V = 0,
+ DA9055_MICBIAS_1_8V = 1,
+ DA9055_MICBIAS_2_1V = 2,
+ DA9055_MICBIAS_2_2V = 3,
+};
+
+struct da9055_platform_data {
+ /* Selects which of the two MicBias pins acts as the bias source */
+ bool micbias_source;
+ /* Selects the micbias voltage */
+ enum da9055_micbias_voltage micbias;
+};
+
+#endif
unsigned int efx_voices_mask[2];
unsigned int next_free_voice;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned int *saved_ptr;
unsigned int *saved_gpr;
unsigned int *tram_val_saved;
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data);
unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu);
void snd_emu10k1_resume_init(struct snd_emu10k1 *emu);
void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu);
#define SNDRV_DEFAULT_DMA_SIZE { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE }
#define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR
+#ifdef SNDRV_LEGACY_FIND_FREE_IOPORT
+static long snd_legacy_find_free_ioport(long *port_table, long size)
+{
+ while (*port_table != -1) {
+ if (request_region(*port_table, size, "ALSA test")) {
+ release_region(*port_table, size);
+ return *port_table;
+ }
+ port_table++;
+ }
+ return -1;
+}
+#endif
+
#ifdef SNDRV_LEGACY_FIND_FREE_IRQ
#include <linux/interrupt.h>
/*
* return the physical address at the corresponding offset
*/
-static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
addr &= PAGE_MASK;
return addr + offset % PAGE_SIZE;
/*
* return the virtual address at the corresponding offset
*/
-static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+ size_t offset)
{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
}
+
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size);
+#else
+/* non-SG versions */
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return dmab->addr + offset;
+}
+
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return dmab->area + offset;
+}
+
+#define snd_sgbuf_get_chunk_size(dmab, ofs, size) (size)
+
#endif /* CONFIG_SND_DMA_SGBUF */
/* allocate/release a buffer */
struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
+ struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
};
struct snd_pcm {
_snd_pcm_lib_alloc_vmalloc_buffer \
(subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO)
+#define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p)
+
#ifdef CONFIG_SND_DMA_SGBUF
/*
* SG-buffer handling
*/
#define snd_pcm_substream_sgbuf(substream) \
- ((substream)->runtime->dma_buffer_p->private_data)
-
-static inline dma_addr_t
-snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- return snd_sgbuf_get_addr(sg, ofs);
-}
-
-static inline void *
-snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- return snd_sgbuf_get_ptr(sg, ofs);
-}
+ snd_pcm_get_dma_buf(substream)->private_data
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned long offset);
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size);
-
#else /* !SND_DMA_SGBUF */
/*
* fake using a continuous buffer
*/
+#define snd_pcm_sgbuf_ops_page NULL
+#endif /* SND_DMA_SGBUF */
+
static inline dma_addr_t
snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
{
- return substream->runtime->dma_addr + ofs;
+ return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs);
}
static inline void *
snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
{
- return substream->runtime->dma_area + ofs;
+ return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs);
}
-#define snd_pcm_sgbuf_ops_page NULL
-
-#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size) (size)
-
-#endif /* SND_DMA_SGBUF */
+static inline unsigned int
+snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+ unsigned int ofs, unsigned int size)
+{
+ return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size);
+}
/* handle mmap counter - PCM mmap callback should handle this counter properly */
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
return "Capture";
}
+/*
+ * PCM channel-mapping control API
+ */
+/* array element of channel maps */
+struct snd_pcm_chmap_elem {
+ unsigned char channels;
+ unsigned char map[15];
+};
+
+/* channel map information; retrieved via snd_kcontrol_chip() */
+struct snd_pcm_chmap {
+ struct snd_pcm *pcm; /* assigned PCM instance */
+ int stream; /* PLAYBACK or CAPTURE */
+ struct snd_kcontrol *kctl;
+ const struct snd_pcm_chmap_elem *chmap;
+ unsigned int max_channels;
+ unsigned int channel_mask; /* optional: active channels bitmask */
+ void *private_data; /* optional: private data pointer */
+};
+
+/* get the PCM substream assigned to the given chmap info */
+static inline struct snd_pcm_substream *
+snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx)
+{
+ struct snd_pcm_substream *s;
+ for (s = info->pcm->streams[info->stream].substream; s; s = s->next)
+ if (s->number == idx)
+ return s;
+ return NULL;
+}
+
+/* ALSA-standard channel maps (RL/RR prior to C/LFE) */
+extern const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[];
+/* Other world's standard channel maps (C/LFE prior to RL/RR) */
+extern const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[];
+
+/* bit masks to be passed to snd_pcm_chmap.channel_mask field */
+#define SND_PCM_CHMAP_MASK_24 ((1U << 2) | (1U << 4))
+#define SND_PCM_CHMAP_MASK_246 (SND_PCM_CHMAP_MASK_24 | (1U << 6))
+#define SND_PCM_CHMAP_MASK_2468 (SND_PCM_CHMAP_MASK_246 | (1U << 8))
+
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret);
+
#endif /* __SOUND_PCM_H */
struct snd_pcm_substream;
struct snd_soc_dapm_widget;
+struct snd_compr_stream;
/*
* DAI hardware audio formats.
int (*remove)(struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai);
+ /* compress dai */
+ bool compress_dai;
/* ops */
const struct snd_soc_dai_ops *ops;
{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .event = wevent, \
.event_flags = wflags}
-#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay) \
+#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \
{ .id = snd_soc_dapm_regulator_supply, .name = wname, \
.reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
- .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .invert = wflags}
/* dapm kcontrol types */
#define SND_SOC_DAPM_EVENT_OFF(e) \
(e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD))
+/* regulator widget flags */
+#define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */
+
struct snd_soc_dapm_widget;
enum snd_soc_dapm_type;
struct snd_soc_dapm_path;
/* Mostly internal - should not normally be used */
void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason);
+void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
/* dapm path query */
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
/* dapm control */
int reg; /* negative reg = no direct dapm */
unsigned char shift; /* bits to shift */
- unsigned int saved_value; /* widget saved value */
unsigned int value; /* widget current value */
unsigned int mask; /* non-shifted mask */
unsigned int on_val; /* on state value */
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
+#include <linux/log2.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/compress_driver.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
.platform_max = xmax} }
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
- .max = xmax, .texts = xtexts }
+ .max = xmax, .texts = xtexts, \
+ .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
int snd_soc_platform_write(struct snd_soc_platform *platform,
unsigned int reg, unsigned int val);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
+int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num);
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
const char *dai_link, int stream);
int (*trigger)(struct snd_pcm_substream *, int);
};
+struct snd_soc_compr_ops {
+ int (*startup)(struct snd_compr_stream *);
+ void (*shutdown)(struct snd_compr_stream *);
+ int (*set_params)(struct snd_compr_stream *);
+ int (*trigger)(struct snd_compr_stream *);
+};
+
/* SoC cache ops */
struct snd_soc_cache_ops {
const char *name;
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
struct snd_soc_dai *);
- /* platform stream ops */
+ /* platform stream pcm ops */
struct snd_pcm_ops *ops;
+ /* platform stream compress ops */
+ struct snd_compr_ops *compr_ops;
+
/* platform stream completion event */
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
/* machine stream operations */
struct snd_soc_ops *ops;
+ struct snd_soc_compr_ops *compr_ops;
};
struct snd_soc_codec_conf {
/* runtime devices */
struct snd_pcm *pcm;
+ struct snd_compr *compr;
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai;
--- /dev/null
+/*
+ * Copyright 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOUND_TEGRA_WM38903_H
+#define __SOUND_TEGRA_WM38903_H
+
+struct tegra_wm8903_platform_data {
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
+};
+
+#endif
#define TLV_DB_GAIN_MUTE -9999999
+/*
+ * channel-mapping TLV items
+ * TLV length must match with num_channels
+ */
+#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */
+#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
+#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
+
#endif /* __SOUND_TLV_H */
+++ /dev/null
-/* include/version.h */
-#define CONFIG_SND_VERSION "1.0.25"
-#define CONFIG_SND_DATE ""
--- /dev/null
+/*
+ * wm0010.h -- Platform data for WM0010 DSP Driver
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef WM0010_PDATA_H
+#define WM0010_PDATA_H
+
+struct wm0010_pdata {
+ int gpio_reset;
+
+ /* Set if there is an inverter between the GPIO controlling
+ * the reset signal and the device.
+ */
+ int reset_active_high;
+ int irq_flags;
+};
+
+#endif
struct wm8960_data {
bool capless; /* Headphone outputs configured in capless mode */
- int dres; /* Discharge resistance for headphone outputs */
+ bool shared_lrclk; /* DAC and ADC LRCLKs are wired together */
};
#endif
unsigned int lineout1fb:1;
unsigned int lineout2fb:1;
+ /* Delay to add for microphones to stabalise after power up */
+ int micbias1_delay;
+ int micbias2_delay;
+
/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
unsigned int micbias1_lvl:1;
unsigned int micbias2_lvl:1;
return 0;
}
+static int snd_compress_check_input(struct snd_compr_params *params)
+{
+ /* first let's check the buffer parameter's */
+ if (params->buffer.fragment_size == 0 ||
+ params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size)
+ return -EINVAL;
+
+ /* now codec parameters */
+ if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
+ return -EINVAL;
+
+ if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
+ return -EINVAL;
+
+ if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000))
+ return -EINVAL;
+
+ return 0;
+}
+
static int
snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
{
retval = -EFAULT;
goto out;
}
+
+ retval = snd_compress_check_input(params);
+ if (retval)
+ goto out;
+
retval = snd_compr_allocate_buffer(stream, params);
if (retval) {
retval = -ENOMEM;
goto out;
}
+
retval = stream->ops->set_params(stream, params);
if (retval)
goto out;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
+#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
#include <stdarg.h>
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
snd_iprintf(buffer,
- "Advanced Linux Sound Architecture Driver Version "
- CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
- );
+ "Advanced Linux Sound Architecture Driver Version k%s.\n",
+ init_utsname()->release);
}
static int __init snd_info_version_init(void)
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <linux/utsname.h>
#include <linux/mutex.h>
static void snd_sndstat_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+ snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA emulation code)\n");
snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
init_utsname()->sysname,
init_utsname()->nodename,
if (kctl->info(kctl, uinfo)) {
up_read(&mixer->card->controls_rwsem);
+ kfree(uinfo);
return 0;
}
strcpy(str, ptr->name);
uinfo->value.enumerated.item = slot.capture_item;
if (kctl->info(kctl, uinfo)) {
up_read(&mixer->card->controls_rwsem);
+ kfree(uinfo);
return 0;
}
if (!strcmp(uinfo->value.enumerated.name, str)) {
break;
}
snd_unregister_device(devtype, pcm->card, pcm->device);
+ if (pcm->streams[cidx].chmap_kctl) {
+ snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
+ pcm->streams[cidx].chmap_kctl = NULL;
+ }
}
unlock:
mutex_unlock(®ister_mutex);
#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
}
EXPORT_SYMBOL(snd_pcm_lib_readv);
+
+/*
+ * standard channel mapping helpers
+ */
+
+/* default channel maps for multi-channel playbacks, up to 8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
+
+/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
+
+static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
+{
+ if (ch > info->max_channels)
+ return false;
+ return !info->channel_mask || (info->channel_mask & (1U << ch));
+}
+
+static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 0;
+ uinfo->count = info->max_channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+/* get callback for channel map ctl element
+ * stores the channel position firstly matching with the current channels
+ */
+static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+
+ if (snd_BUG_ON(!info->chmap))
+ return -EINVAL;
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (!substream->runtime)
+ return 0; /* no channels set */
+ for (map = info->chmap; map->channels; map++) {
+ int i;
+ if (map->channels == substream->runtime->channels &&
+ valid_chmap_channels(info, map->channels)) {
+ for (i = 0; i < map->channels; i++)
+ ucontrol->value.integer.value[i] = map->map[i];
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* tlv callback for channel map ctl element
+ * expands the pre-defined channel maps in a form of TLV
+ */
+static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (snd_BUG_ON(!info->chmap))
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+ if (!valid_chmap_channels(info, map->channels))
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
+ return -EFAULT;
+ dst++;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].chmap_kctl = NULL;
+ kfree(info);
+}
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
+{
+ struct snd_pcm_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = pcm_chmap_ctl_info,
+ .get = pcm_chmap_ctl_get,
+ .tlv.c = pcm_chmap_ctl_tlv,
+ };
+ int err;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->chmap = chmap;
+ info->max_channels = max_channels;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ knew.name = "Playback Channel Map";
+ else
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0)
+ return err;
+ pcm->streams[stream].chmap_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
}
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
- */
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size)
-{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- unsigned int start, end, pg;
-
- start = ofs >> PAGE_SHIFT;
- end = (ofs + size - 1) >> PAGE_SHIFT;
- /* check page continuity */
- pg = sg->table[start].addr >> PAGE_SHIFT;
- for (;;) {
- start++;
- if (start > end)
- break;
- pg++;
- if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
- return (start << PAGE_SHIFT) - ofs;
- }
- /* ok, all on continuous pages */
- return size;
-}
-EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
#endif /* CONFIG_SND_DMA_SGBUF */
/**
EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
+#ifdef CONFIG_MODULES
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
+#endif
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/memalloc.h>
snd_free_sgbuf_pages(dmab); /* free the table */
return NULL;
}
+
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ struct snd_sg_buf *sg = dmab->private_data;
+ unsigned int start, end, pg;
+
+ start = ofs >> PAGE_SHIFT;
+ end = (ofs + size - 1) >> PAGE_SHIFT;
+ /* check page continuity */
+ pg = sg->table[start].addr >> PAGE_SHIFT;
+ for (;;) {
+ start++;
+ if (start > end)
+ break;
+ pg++;
+ if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+ return (start << PAGE_SHIFT) - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
}
snd_info_minor_register();
#ifndef MODULE
- printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
+ printk(KERN_INFO "Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
return 0;
}
unsigned int last_drift;
unsigned long last_jiffies;
struct timer_list timer;
+ spinlock_t timer_lock;
};
static struct platform_device *devices[SNDRV_CARDS];
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
+ spin_lock(&dpcm->timer_lock);
if (rate_shift != dpcm->pcm_rate_shift) {
dpcm->pcm_rate_shift = rate_shift;
dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
dpcm->timer.expires = jiffies + tick;
add_timer(&dpcm->timer);
+ spin_unlock(&dpcm->timer_lock);
}
static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
{
+ spin_lock(&dpcm->timer_lock);
del_timer(&dpcm->timer);
dpcm->timer.expires = 0;
+ spin_unlock(&dpcm->timer_lock);
}
#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
dpcm->substream = substream;
setup_timer(&dpcm->timer, loopback_timer_function,
(unsigned long)dpcm);
+ spin_lock_init(&dpcm->timer_lock);
cable = loopback->cables[substream->number][dev];
if (!cable) {
struct best *bp;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
spin_lock_irqsave(&opl4->reg_lock, flags);
for (i = 0; i < voices; i++) {
voice[i] = snd_opl4_get_voice(opl4);
- list_del(&voice[i]->list);
- list_add_tail(&voice[i]->list, &opl4->on_voices);
+ list_move_tail(&voice[i]->list, &opl4->on_voices);
voice[i]->chan = chan;
voice[i]->note = note;
voice[i]->velocity = vel & 0x7f;
static void snd_opl4_voice_off(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc &= ~OPL4_KEY_ON_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
static void snd_opl4_terminate_voice(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc = (voice->reg_misc & ~OPL4_KEY_ON_BIT) | OPL4_DAMP_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
default :
snd_BUG();
return -EINVAL;
- };
+ }
return vx_set_stream_format(chip, pipe, header);
}
To compile this driver as a module, choose M here: the module
will be called snd-azt2320.
+config SND_CMI8328
+ tristate "C-Media CMI8328"
+ select SND_WSS_LIB
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ help
+ Say Y here to include support for soundcards based on the
+ C-Media CMI8328 chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cmi8328.
+
config SND_CMI8330
tristate "C-Media CMI8330"
select SND_WSS_LIB
snd-adlib-objs := adlib.o
snd-als100-objs := als100.o
snd-azt2320-objs := azt2320.o
+snd-cmi8328-objs := cmi8328.o
snd-cmi8330-objs := cmi8330.o
snd-es18xx-objs := es18xx.o
snd-opl3sa2-objs := opl3sa2.o
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
obj-$(CONFIG_SND_ALS100) += snd-als100.o
obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
+obj-$(CONFIG_SND_CMI8328) += snd-cmi8328.o
obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
module_param_array(clockfreq, int, NULL, 0444);
MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
-struct snd_card_ad1816a {
- struct pnp_dev *dev;
- struct pnp_dev *devmpu;
-};
-
static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
/* Analog Devices AD1815 */
{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
#define DRIVER_NAME "snd-card-ad1816a"
-static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acard,
- struct pnp_card_link *card,
+static int __devinit snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
- acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
- if (acard->dev == NULL)
+ pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (pdev == NULL)
return -EBUSY;
- acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
- if (acard->devmpu == NULL) {
- mpu_port[dev] = -1;
- snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
- }
-
- pdev = acard->dev;
-
err = pnp_activate_dev(pdev);
if (err < 0) {
printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- if (acard->devmpu == NULL)
+ pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (pdev == NULL) {
+ mpu_port[dev] = -1;
+ snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
return 0;
-
- pdev = acard->devmpu;
+ }
err = pnp_activate_dev(pdev);
if (err < 0) {
printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
mpu_port[dev] = -1;
- acard->devmpu = NULL;
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
mpu_irq[dev] = pnp_irq(pdev, 0);
{
int error;
struct snd_card *card;
- struct snd_card_ad1816a *acard;
struct snd_ad1816a *chip;
struct snd_opl3 *opl3;
struct snd_timer *timer;
error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_ad1816a), &card);
+ sizeof(struct snd_ad1816a), &card);
if (error < 0)
return error;
- acard = card->private_data;
+ chip = card->private_data;
- if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
+ if ((error = snd_card_ad1816a_pnp(dev, pcard, pid))) {
snd_card_free(card);
return error;
}
irq[dev],
dma1[dev],
dma2[dev],
- &chip)) < 0) {
+ chip)) < 0) {
snd_card_free(card);
return error;
}
pnp_set_card_drvdata(pcard, NULL);
}
+#ifdef CONFIG_PM
+static int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_ad1816a_suspend(card->private_data);
+ return 0;
+}
+
+static int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_ad1816a_resume(card->private_data);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
static struct pnp_card_driver ad1816a_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "ad1816a",
.id_table = snd_ad1816a_pnpids,
.probe = snd_ad1816a_pnp_detect,
.remove = __devexit_p(snd_ad1816a_pnp_remove),
- /* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+ .suspend = snd_ad1816a_pnp_suspend,
+ .resume = snd_ad1816a_pnp_resume,
+#endif
};
static int __init alsa_card_ad1816a_init(void)
}
-static void __devinit snd_ad1816a_init(struct snd_ad1816a *chip)
+static void snd_ad1816a_init(struct snd_ad1816a *chip)
{
unsigned long flags;
spin_unlock_irqrestore(&chip->lock, flags);
}
+#ifdef CONFIG_PM
+void snd_ad1816a_suspend(struct snd_ad1816a *chip)
+{
+ int reg;
+ unsigned long flags;
+
+ snd_pcm_suspend_all(chip->pcm);
+ spin_lock_irqsave(&chip->lock, flags);
+ for (reg = 0; reg < 48; reg++)
+ chip->image[reg] = snd_ad1816a_read(chip, reg);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+void snd_ad1816a_resume(struct snd_ad1816a *chip)
+{
+ int reg;
+ unsigned long flags;
+
+ snd_ad1816a_init(chip);
+ spin_lock_irqsave(&chip->lock, flags);
+ for (reg = 0; reg < 48; reg++)
+ snd_ad1816a_write(chip, reg, chip->image[reg]);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+#endif
+
static int __devinit snd_ad1816a_probe(struct snd_ad1816a *chip)
{
unsigned long flags;
snd_dma_disable(chip->dma2);
free_dma(chip->dma2);
}
- kfree(chip);
return 0;
}
int __devinit snd_ad1816a_create(struct snd_card *card,
unsigned long port, int irq, int dma1, int dma2,
- struct snd_ad1816a **rchip)
+ struct snd_ad1816a *chip)
{
static struct snd_device_ops ops = {
.dev_free = snd_ad1816a_dev_free,
};
int error;
- struct snd_ad1816a *chip;
-
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
return error;
}
- *rchip = chip;
return 0;
}
--- /dev/null
+/*
+ * Driver for C-Media CMI8328-based soundcards, such as AudioExcel AV500
+ * Copyright (c) 2012 Ondrej Zary
+ *
+ * AudioExcel AV500 card consists of:
+ * - CMI8328 - main chip (SB Pro emulation, gameport, OPL3, MPU401, CD-ROM)
+ * - CS4231A - WSS codec
+ * - Dream SAM9233+GMS950400+RAM+ROM: Wavetable MIDI, connected to MPU401
+ */
+
+#include <linux/init.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/wss.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Ondrej Zary <linux@rainbow-software.org>");
+MODULE_DESCRIPTION("C-Media CMI8328");
+MODULE_LICENSE("GPL");
+
+#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
+#define SUPPORT_JOYSTICK 1
+#endif
+
+/* I/O port is configured by jumpers on the card to one of these */
+static int cmi8328_ports[] = { 0x530, 0xe80, 0xf40, 0x604 };
+#define CMI8328_MAX ARRAY_SIZE(cmi8328_ports)
+
+static int index[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = -1};
+static char *id[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = NULL};
+static long port[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int irq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+static int dma1[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static int dma2[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static long mpuport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int mpuirq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+#ifdef SUPPORT_JOYSTICK
+static bool gameport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = true};
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CMI8328 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CMI8328 soundcard.");
+
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for CMI8328 driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for CMI8328 driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "DMA1 for CMI8328 driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "DMA2 for CMI8328 driver.");
+
+module_param_array(mpuport, long, NULL, 0444);
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8328 driver.");
+module_param_array(mpuirq, int, NULL, 0444);
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8328 MPU-401 port.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(gameport, bool, NULL, 0444);
+MODULE_PARM_DESC(gameport, "Enable gameport.");
+#endif
+
+struct snd_cmi8328 {
+ u16 port;
+ u8 cfg[3];
+ u8 wss_cfg;
+ struct snd_card *card;
+ struct snd_wss *wss;
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+/* CMI8328 configuration registers */
+#define CFG1 0x61
+#define CFG1_SB_DISABLE (1 << 0)
+#define CFG1_GAMEPORT (1 << 1)
+/*
+ * bit 0: SB: 0=enabled, 1=disabled
+ * bit 1: gameport: 0=disabled, 1=enabled
+ * bits 2-4: SB IRQ: 001=3, 010=5, 011=7, 100=9, 101=10, 110=11
+ * bits 5-6: SB DMA: 00=disabled (when SB disabled), 01=DMA0, 10=DMA1, 11=DMA3
+ * bit 7: SB port: 0=0x220, 1=0x240
+ */
+#define CFG2 0x62
+#define CFG2_MPU_ENABLE (1 << 2)
+/*
+ * bits 0-1: CD-ROM mode: 00=disabled, 01=Panasonic, 10=Sony/Mitsumi/Wearnes,
+ 11=IDE
+ * bit 2: MPU401: 0=disabled, 1=enabled
+ * bits 3-4: MPU401 IRQ: 00=3, 01=5, 10=7, 11=9,
+ * bits 5-7: MPU401 port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x332,
+ 101=0x334, 110=0x336
+ */
+#define CFG3 0x63
+/*
+ * bits 0-2: CD-ROM IRQ: 000=disabled, 001=3, 010=5, 011=7, 100=9, 101=10,
+ 110=11
+ * bits 3-4: CD-ROM DMA: 00=disabled, 01=DMA0, 10=DMA1, 11=DMA3
+ * bits 5-7: CD-ROM port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x340,
+ 101=0x350, 110=0x360, 111=0x370
+ */
+
+static u8 snd_cmi8328_cfg_read(u16 port, u8 reg)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ return inb(port);
+}
+
+static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ outb(val, port + 3); /* yes, value goes to the same port as index */
+}
+
+static void snd_cmi8328_cfg_save(u16 port, u8 cfg[])
+{
+ cfg[0] = snd_cmi8328_cfg_read(port, CFG1);
+ cfg[1] = snd_cmi8328_cfg_read(port, CFG2);
+ cfg[2] = snd_cmi8328_cfg_read(port, CFG3);
+}
+
+static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[])
+{
+ snd_cmi8328_cfg_write(port, CFG1, cfg[0]);
+ snd_cmi8328_cfg_write(port, CFG2, cfg[1]);
+ snd_cmi8328_cfg_write(port, CFG3, cfg[2]);
+}
+
+static int __devinit snd_cmi8328_mixer(struct snd_wss *chip)
+{
+ struct snd_card *card;
+ struct snd_ctl_elem_id id1, id2;
+ int err;
+
+ card = chip->card;
+
+ memset(&id1, 0, sizeof(id1));
+ memset(&id2, 0, sizeof(id2));
+ id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ /* rename AUX0 switch to CD */
+ strcpy(id1.name, "Aux Playback Switch");
+ strcpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX0 volume to CD */
+ strcpy(id1.name, "Aux Playback Volume");
+ strcpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 switch to Synth */
+ strcpy(id1.name, "Aux Playback Switch");
+ id1.index = 1;
+ strcpy(id2.name, "Synth Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 volume to Synth */
+ strcpy(id1.name, "Aux Playback Volume");
+ id1.index = 1;
+ strcpy(id2.name, "Synth Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ snd_printk(KERN_ERR "error renaming control\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/* find index of an item in "-1"-ended array */
+int array_find(int array[], int item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+/* the same for long */
+int array_find_l(long array[], long item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+
+static int __devinit snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
+{
+ struct snd_card *card;
+ struct snd_opl3 *opl3;
+ struct snd_cmi8328 *cmi;
+#ifdef SUPPORT_JOYSTICK
+ struct resource *res;
+#endif
+ int err, pos;
+ static long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334,
+ 0x336, -1 };
+ static u8 mpu_port_bits[] = { 3, 0, 1, 2, 4, 5, 6 };
+ static int mpu_irqs[] = { 9, 7, 5, 3, -1 };
+ static u8 mpu_irq_bits[] = { 3, 2, 1, 0 };
+ static int irqs[] = { 9, 10, 11, 7, -1 };
+ static u8 irq_bits[] = { 2, 3, 4, 1 };
+ static int dma1s[] = { 3, 1, 0, -1 };
+ static u8 dma_bits[] = { 3, 2, 1 };
+ static int dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, {0, -1} };
+ u16 port = cmi8328_ports[ndev];
+ u8 val;
+
+ /* 0xff is invalid configuration (but settable - hope it isn't set) */
+ if (snd_cmi8328_cfg_read(port, CFG1) == 0xff)
+ return -ENODEV;
+ /* the SB disable bit must NEVER EVER be cleared or the WSS dies */
+ snd_cmi8328_cfg_write(port, CFG1, CFG1_SB_DISABLE);
+ if (snd_cmi8328_cfg_read(port, CFG1) != CFG1_SB_DISABLE)
+ return -ENODEV;
+ /* disable everything first */
+ snd_cmi8328_cfg_write(port, CFG2, 0); /* disable CDROM and MPU401 */
+ snd_cmi8328_cfg_write(port, CFG3, 0); /* disable CDROM IRQ and DMA */
+
+ if (irq[ndev] == SNDRV_AUTO_IRQ) {
+ irq[ndev] = snd_legacy_find_free_irq(irqs);
+ if (irq[ndev] < 0) {
+ snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (dma1[ndev] == SNDRV_AUTO_DMA) {
+ dma1[ndev] = snd_legacy_find_free_dma(dma1s);
+ if (dma1[ndev] < 0) {
+ snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ return -EBUSY;
+ }
+ }
+ if (dma2[ndev] == SNDRV_AUTO_DMA) {
+ dma2[ndev] = snd_legacy_find_free_dma(dma2s[dma1[ndev] % 4]);
+ if (dma2[ndev] < 0) {
+ snd_printk(KERN_WARNING "unable to find a free DMA2, full-duplex will not work\n");
+ dma2[ndev] = -1;
+ }
+ }
+ /* configure WSS IRQ... */
+ pos = array_find(irqs, irq[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid IRQ %d\n", irq[ndev]);
+ return -EINVAL;
+ }
+ val = irq_bits[pos] << 3;
+ /* ...and DMA... */
+ pos = array_find(dma1s, dma1[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid DMA1 %d\n", dma1[ndev]);
+ return -EINVAL;
+ }
+ val |= dma_bits[pos];
+ /* ...and DMA2 */
+ if (dma2[ndev] >= 0 && dma1[ndev] != dma2[ndev]) {
+ pos = array_find(dma2s[dma1[ndev]], dma2[ndev]);
+ if (pos < 0) {
+ snd_printk(KERN_ERR "invalid DMA2 %d\n", dma2[ndev]);
+ return -EINVAL;
+ }
+ val |= 0x04; /* enable separate capture DMA */
+ }
+ outb(val, port);
+
+ err = snd_card_create(index[ndev], id[ndev], THIS_MODULE,
+ sizeof(struct snd_cmi8328), &card);
+ if (err < 0)
+ return err;
+ cmi = card->private_data;
+ cmi->card = card;
+ cmi->port = port;
+ cmi->wss_cfg = val;
+ snd_card_set_dev(card, pdev);
+
+ err = snd_wss_create(card, port + 4, -1, irq[ndev], dma1[ndev],
+ dma2[ndev], WSS_HW_DETECT, 0, &cmi->wss);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_pcm(cmi->wss, 0, NULL);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_mixer(cmi->wss);
+ if (err < 0)
+ goto error;
+ err = snd_cmi8328_mixer(cmi->wss);
+ if (err < 0)
+ goto error;
+
+ if (snd_wss_timer(cmi->wss, 0, NULL) < 0)
+ snd_printk(KERN_WARNING "error initializing WSS timer\n");
+
+ if (mpuport[ndev] == SNDRV_AUTO_PORT) {
+ mpuport[ndev] = snd_legacy_find_free_ioport(mpu_ports, 2);
+ if (mpuport[ndev] < 0)
+ snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
+ }
+ if (mpuirq[ndev] == SNDRV_AUTO_IRQ) {
+ mpuirq[ndev] = snd_legacy_find_free_irq(mpu_irqs);
+ if (mpuirq[ndev] < 0)
+ snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
+ }
+ /* enable and configure MPU401 */
+ if (mpuport[ndev] > 0 && mpuirq[ndev] > 0) {
+ val = CFG2_MPU_ENABLE;
+ pos = array_find_l(mpu_ports, mpuport[ndev]);
+ if (pos < 0)
+ snd_printk(KERN_WARNING "invalid MPU401 port 0x%lx\n",
+ mpuport[ndev]);
+ else {
+ val |= mpu_port_bits[pos] << 5;
+ pos = array_find(mpu_irqs, mpuirq[ndev]);
+ if (pos < 0)
+ snd_printk(KERN_WARNING "invalid MPU401 IRQ %d\n",
+ mpuirq[ndev]);
+ else {
+ val |= mpu_irq_bits[pos] << 3;
+ snd_cmi8328_cfg_write(port, CFG2, val);
+ if (snd_mpu401_uart_new(card, 0,
+ MPU401_HW_MPU401, mpuport[ndev],
+ 0, mpuirq[ndev], NULL) < 0)
+ snd_printk(KERN_ERR "error initializing MPU401\n");
+ }
+ }
+ }
+ /* OPL3 is hardwired to 0x388 and cannot be disabled */
+ if (snd_opl3_create(card, 0x388, 0x38a, OPL3_HW_AUTO, 0, &opl3) < 0)
+ snd_printk(KERN_ERR "error initializing OPL3\n");
+ else
+ if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0)
+ snd_printk(KERN_WARNING "error initializing OPL3 hwdep\n");
+
+ strcpy(card->driver, "CMI8328");
+ strcpy(card->shortname, "C-Media CMI8328");
+ sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d",
+ card->shortname, cmi->wss->port, irq[ndev], dma1[ndev],
+ (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]);
+
+ dev_set_drvdata(pdev, card);
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+#ifdef SUPPORT_JOYSTICK
+ if (!gameport[ndev])
+ return 0;
+ /* gameport is hardwired to 0x200 */
+ res = request_region(0x200, 8, "CMI8328 gameport");
+ if (!res)
+ snd_printk(KERN_WARNING "unable to allocate gameport I/O port\n");
+ else {
+ struct gameport *gp = cmi->gameport = gameport_allocate_port();
+ if (!cmi->gameport)
+ release_and_free_resource(res);
+ else {
+ gameport_set_name(gp, "CMI8328 Gameport");
+ gameport_set_phys(gp, "%s/gameport0", dev_name(pdev));
+ gameport_set_dev_parent(gp, pdev);
+ gp->io = 0x200;
+ gameport_set_port_data(gp, res);
+ /* Enable gameport */
+ snd_cmi8328_cfg_write(port, CFG1,
+ CFG1_SB_DISABLE | CFG1_GAMEPORT);
+ gameport_register_port(gp);
+ }
+ }
+#endif
+ return 0;
+error:
+ snd_card_free(card);
+
+ return err;
+}
+
+static int __devexit snd_cmi8328_remove(struct device *pdev, unsigned int dev)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi = card->private_data;
+
+#ifdef SUPPORT_JOYSTICK
+ if (cmi->gameport) {
+ struct resource *res = gameport_get_port_data(cmi->gameport);
+ gameport_unregister_port(cmi->gameport);
+ release_and_free_resource(res);
+ }
+#endif
+ /* disable everything */
+ snd_cmi8328_cfg_write(cmi->port, CFG1, CFG1_SB_DISABLE);
+ snd_cmi8328_cfg_write(cmi->port, CFG2, 0);
+ snd_cmi8328_cfg_write(cmi->port, CFG3, 0);
+ snd_card_free(card);
+ dev_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_cmi8328_suspend(struct device *pdev, unsigned int n,
+ pm_message_t state)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_save(cmi->port, cmi->cfg);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(cmi->wss->pcm);
+ cmi->wss->suspend(cmi->wss);
+
+ return 0;
+}
+
+static int snd_cmi8328_resume(struct device *pdev, unsigned int n)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_restore(cmi->port, cmi->cfg);
+ outb(cmi->wss_cfg, cmi->port);
+ cmi->wss->resume(cmi->wss);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif
+
+static struct isa_driver snd_cmi8328_driver = {
+ .probe = snd_cmi8328_probe,
+ .remove = __devexit_p(snd_cmi8328_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_cmi8328_suspend,
+ .resume = snd_cmi8328_resume,
+#endif
+ .driver = {
+ .name = "cmi8328"
+ },
+};
+
+static int __init alsa_card_cmi8328_init(void)
+{
+ return isa_register_driver(&snd_cmi8328_driver, CMI8328_MAX);
+}
+
+static void __exit alsa_card_cmi8328_exit(void)
+{
+ isa_unregister_driver(&snd_cmi8328_driver);
+}
+
+module_init(alsa_card_cmi8328_init)
+module_exit(alsa_card_cmi8328_exit)
for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i);
#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos,
- iwave[0], iwave[1], iwave[2], iwave[3],
- iwave[4], iwave[5], iwave[6], iwave[7]);
+ printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos,
+ 8, iwave);
#endif
if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */
#include <sound/opl4.h>
#include <sound/control.h>
#include <sound/info.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
return 0;
}
-static long snd_legacy_find_free_ioport(long *port_table, long size)
-{
- while (*port_table != -1) {
- struct resource *res;
- if ((res = request_region(*port_table, size,
- "ALSA test")) != NULL) {
- release_and_free_resource(res);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
static int __devinit snd_miro_init(struct snd_miro *chip,
unsigned short hardware)
{
#ifndef OPTi93X
#include <sound/opl4.h>
#endif
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
"82C930", "82C931", "82C933"
};
-
-static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)
-{
- while (*port_table != -1) {
- if (request_region(*port_table, size, "ALSA test")) {
- release_region(*port_table, size);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
unsigned short hardware)
{
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
break; /* no memory at this address */
-
- detected_size = size;
-
snd_emu8000_read_wait(emu);
/*
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
break; /* we must have wrapped around */
snd_emu8000_read_wait(emu);
+
+ /* Otherwise, it's valid memory. */
+ detected_size = size + 512 * 1024;
+ }
+
+ /* Distinguish 512 KiB from 0. */
+ if (detected_size == 0) {
+ snd_emu8000_read_wait(emu);
+ EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+ EMU8000_SMLD_READ(emu); /* discard stale data */
+ if (EMU8000_SMLD_READ(emu) == UNIQUE_ID1)
+ detected_size = 512 * 1024;
}
/* wait until FULL bit in SMAxW register is false */
hw = emu->hw;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
/* Note: we leave the upper bits in place */
dst++;
- };
+ }
return dst;
};
*
*/
-#define SNDRV_MAIN_OBJECT_FILE
#include <linux/init.h>
#include <sound/core.h>
if(copy_to_user(&(buf)[p], fixit, l))
return -EFAULT;
- };
+ }
DMAbuf_rmchars(dev, buf_no, l);
for (i = 0; i < 18; i++)
pv_map[i].ioaddr = devc->left_io;
- };
+ }
conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1);
for (i = 0; i < SBFM_MAXINSTR; i++)
{
/*_____ Send the next byte */
outw (*block++, REG (PSS_DATA));
- };
+ }
count++;
}
default:
printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
return 0;
- };
+ }
if (submodel != -1) {
devc->submodel = submodel;
sprintf (modelname, "ES%d", devc->sbmo.esstype);
chip = modelname;
- };
+ }
if (chip == NULL && (ess_minor & 0x0f) < 8) {
chip = "ES688";
- };
+ }
#ifdef FKS_TEST
FKS_test (devc);
#endif
*/
if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
chip = "ES1688";
- };
+ }
if (chip == NULL) {
int type;
if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
printk ("ess_init: Unrecognized %04x\n", type);
}
- };
- };
+ }
+ }
#if 0
/*
* this one failed:
chip = "ES1788";
devc->submodel = SUBMDL_ES1788;
}
- };
+ }
if (chip == NULL) {
chip = "ES1688";
- };
+ }
printk ( KERN_INFO "ESS chip %s %s%s\n"
, chip
default:
printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
return 0;
- };
+ }
ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
}
udelay(20);
outb(((unsigned char) (value & 0xff)), MIXER_DATA);
udelay(20);
- };
+ }
spin_unlock_irqrestore(&devc->lock, flags);
}
ess_chgmixer(devc, 0x7a, 0x18, 0x08);
ess_chgmixer(devc, 0x1c, 0x07, 0x07);
break;
- };
+ }
/*
* Call set_recmask for proper initialization
*/
case MDL_SMW:
if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
break;
- };
+ }
if (devmask != SOUND_MASK_MIC &&
devmask != SOUND_MASK_LINE &&
devmask != SOUND_MASK_CD)
if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
set_recmask(devc, SOUND_MASK_MIC);
- };
+ }
}
int sb_mixer_init(sb_devc * devc, struct module *owner)
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
- };
+ }
if (tmr_running)
{
{
def_tmr.expires = (1) + jiffies;
add_timer(&def_tmr);
- };
+ }
return 0;
}
{
/* printk("Midi6850: Midi busy\n");*/
return -EBUSY;
- };
+ }
uart6850_cmd(UART_RESET);
uart6850_input_loop();
shared ? 0 : 0x100);
}
+static int alc650_swap_surround_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_chmap *map = ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (map) {
+ if (ucontrol->value.integer.value[0])
+ map->chmap = snd_pcm_std_chmaps;
+ else
+ map->chmap = snd_pcm_alt_chmaps;
+ }
+ return snd_ac97_put_volsw(kcontrol, ucontrol);
+}
+
static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
/* 9: Line-In/Surround share */
/* 10: Mic/CLFE share */
/* 11-13: in IEC958 controls */
- AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Swap Surround Slot",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_get_volsw,
+ .put = alc650_swap_surround_put,
+ .private_value = AC97_SINGLE_VALUE(AC97_ALC650_MULTICH, 14, 1, 0),
+ },
#if 0 /* always set in patch_alc650 */
AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
spinlock_t reg_lock;
spinlock_t voice_alloc;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
struct snd_ali_image *image;
#endif
};
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int ali_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define ALI_PM_OPS &ali_pm
#else
#define ALI_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int snd_ali_free(struct snd_ali * codec)
{
if (codec->port)
pci_release_regions(codec->pci);
pci_disable_device(codec->pci);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(codec->image);
#endif
pci_dev_put(codec->pci_m1533);
return err;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
if (!codec->image)
snd_printk(KERN_WARNING "can't allocate apm buffer\n");
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_als300_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_als4000_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define SND_ALS4000_PM_OPS &snd_als4000_pm
#else
#define SND_ALS4000_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver als4000_driver = {
.name = KBUILD_MODNAME,
hpi_ctl.dst_node_type,
hpi_ctl.dst_node_index);
continue;
- };
+ }
if (err < 0)
return err;
}
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
.remove = __devexit_p(snd_asihpi_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* .suspend = snd_asihpi_suspend,
.resume = snd_asihpi_resume, */
#endif
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
struct snd_ac97_bus *pbus = chip->ac97_bus;
int err, i, num_pcms;
snd_dma_pci_data(chip->pci),
64*1024, 128*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chip->max_channels, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+
/* no SPDIF support on codec? */
if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define SND_ATIIXP_PM_OPS &snd_atiixp_pm
#else
#define SND_ATIIXP_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PROC_FS
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define SND_ATIIXP_PM_OPS &snd_atiixp_pm
#else
#define SND_ATIIXP_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PROC_FS
/*
if (!gp) {
printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
return -ENOMEM;
- };
+ }
gameport_set_name(gp, "AU88x0 Gameport");
gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
case 4:
mixin = p->mixin[i];
break;
- };
+ }
vol = p->vol[i];
vortex_mix_setinputvolumebyte(vortex,
vortex->mixplayb[i], mixin, vol);
* CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_ctrl_6AH;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* register value containers for power management
* Note: not always full I/O range preserved (similar to Win driver!) */
u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
snd_azf3328_dbgcallleave();
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static inline void
snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
{
#define SND_AZF3328_PM_OPS &snd_azf3328_pm
#else
#define SND_AZF3328_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver azf3328_driver = {
.name = KBUILD_MODNAME,
u16 spi_dac_reg[16];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_SAVED_VOLUMES 9
unsigned int saved_vol[NUM_SAVED_VOLUMES];
#endif
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
unsigned int data);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
#else
return IRQ_HANDLED;
}
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+
static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
case 0:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+ map = snd_pcm_std_chmaps;
break;
case 1:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+ map = surround_map;
break;
case 2:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+ map = clfe_map;
break;
case 3:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+ map = side_map;
break;
}
return err;
}
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
+ if (err < 0)
+ return err;
+
emu->pcm[device] = pcm;
return 0;
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_ca0106_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
struct ca0106_vol_tbl {
unsigned int channel_id;
unsigned int reg;
if (chip->details->i2c_adc)
ca0106_set_capture_mic_line_in(chip);
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
spinlock_t reg_lock;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned int saved_regs[0x20];
unsigned char saved_mixers[0x20];
#endif
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, cm->max_channels, 0,
+ NULL);
+ if (err < 0)
+ return err;
+
return 0;
}
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define SND_CMIPCI_PM_OPS &snd_cmipci_pm
#else
#define SND_CMIPCI_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver cmipci_driver = {
.name = KBUILD_MODNAME,
struct gameport *gameport;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u32 suspend_regs[SUSPEND_REGISTERS];
#endif
/*
* Power Management
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int saved_regs[SUSPEND_REGISTERS] = {
BA0_JSCTL,
#define CS4281_PM_OPS &cs4281_pm
#else
#define CS4281_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver cs4281_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
.remove = __devexit_p(snd_card_cs46xx_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_cs46xx_pm,
},
unsigned int play_ctl;
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u32 *saved_regs;
#endif
};
}
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(chip->saved_regs);
#endif
/*
* APM support
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static unsigned int saved_regs[] = {
BA0_ACOSV,
/*BA0_ASER_FADDR,*/
}
SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume);
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
/*
snd_cs46xx_proc_init(card, chip);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
ARRAY_SIZE(saved_regs), GFP_KERNEL);
if (!chip->saved_regs) {
struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip);
#endif
struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[i].data);
#endif
}
{
struct dsp_scb_descriptor * desc;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* copy the data for resume */
scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL);
if (!scb_data)
_dsp_create_scb(chip,scb_data,dest);
} else {
snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(scb_data);
#endif
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
remove_symbol (chip,scb->scb_symbol);
ins->scbs[scb->index].deleted = 1;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[scb->index].data);
ins->scbs[scb->index].data = NULL;
#endif
mem = pci_ioremap_bar(pci, 0);
if (mem == NULL) {
- kfree(chip);
- pci_disable_device(pci);
+ snd_cs5530_free(chip);
return -EBUSY;
}
#
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
-snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
+snd-cs5535audio-$(CONFIG_PM_SLEEP) += cs5535audio_pm.o
snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
# Toplevel Module Dependency
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
.remove = __devexit_p(snd_cs5535audio_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_cs5535audio_pm,
},
}
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atc_suspend(struct ct_atc *atc)
{
int i;
.output_switch_put = atc_output_switch_put,
.mic_source_switch_get = atc_mic_source_switch_get,
.mic_source_switch_put = atc_mic_source_switch_put,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.suspend = atc_suspend,
.resume = atc_resume,
#endif
struct ct_timer *timer;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*suspend)(struct ct_atc *atc);
int (*resume)(struct ct_atc *atc);
#define NUM_PCMS (NUM_CTALSADEVS - 1)
int (*card_init)(struct hw *hw, struct card_conf *info);
int (*card_stop)(struct hw *hw);
int (*pll_init)(struct hw *hw, unsigned int rsr);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*suspend)(struct hw *hw);
int (*resume)(struct hw *hw, struct card_conf *info);
#endif
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int hw_suspend(struct hw *hw)
{
struct pci_dev *pci = hw->pci;
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
.capabilities = hw_capabilities,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int hw_suspend(struct hw *hw)
{
struct pci_dev *pci = hw->pci;
.output_switch_put = hw_output_switch_put,
.mic_source_switch_get = hw_mic_source_switch_get,
.mic_source_switch_put = hw_mic_source_switch_put,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int mixer_resume(struct ct_mixer *mixer)
{
int i, state;
mixer->get_output_ports = mixer_get_output_ports;
mixer->set_input_left = mixer_set_input_left;
mixer->set_input_right = mixer_set_input_right;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
mixer->resume = mixer_resume;
#endif
enum MIXER_PORT_T type, struct rsc *rsc);
int (*set_input_right)(struct ct_mixer *mixer,
enum MIXER_PORT_T type, struct rsc *rsc);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*resume)(struct ct_mixer *mixer);
#endif
};
.page = snd_pcm_sgbuf_ops_page,
};
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+
/* Create ALSA pcm device */
int ct_alsa_pcm_create(struct ct_atc *atc,
enum CTALSADEVS device,
const char *device_name)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map;
+ int chs;
int err;
int playback_count, capture_count;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
-#ifdef CONFIG_PM
+ chs = 2;
+ switch (device) {
+ case FRONT:
+ chs = 8;
+ map = snd_pcm_std_chmaps;
+ break;
+ case SURROUND:
+ map = surround_map;
+ break;
+ case CLFE:
+ map = clfe_map;
+ break;
+ case SIDE:
+ map = side_map;
+ break;
+ default:
+ map = snd_pcm_std_chmaps;
+ break;
+ }
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
+ 0, NULL);
+ if (err < 0)
+ return err;
+
+#ifdef CONFIG_PM_SLEEP
atc->pcms[device] = pcm;
#endif
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int ct_card_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
int err;
char name[30];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
if (chip->fw_cache[fw_index]) {
DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data));
*fw_entry = chip->fw_cache[fw_index];
err = request_firmware(fw_entry, name, pci_device(chip));
if (err < 0)
snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
else
chip->fw_cache[fw_index] = *fw_entry;
#endif
static void free_firmware(const struct firmware *fw_entry)
{
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
DE_ACT(("firmware not released (kept in cache)\n"));
#else
release_firmware(fw_entry);
static void free_firmware_cache(struct echoaudio *chip)
{
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int i;
for (i = 0; i < 8 ; i++)
-#if defined(CONFIG_PM)
+#if defined(CONFIG_PM_SLEEP)
static int snd_echo_suspend(struct device *dev)
{
#define SND_ECHO_PM_OPS &snd_echo_pm
#else
#define SND_ECHO_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static void __devexit snd_echo_remove(struct pci_dev *pci)
volatile u32 __iomem *dsp_registers; /* DSP's register base */
u32 active_mask; /* Chs. active mask or
* punks out */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
const struct firmware *fw_cache[8]; /* Cached firmwares */
#endif
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_emu10k1_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm
#else
#define SND_EMU10K1_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver emu10k1_driver = {
.name = KBUILD_MODNAME,
int i;
for (i = 0; i < V_END; i++) {
- best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
best[i].voice = -1;
}
* Create the EMU10K1 instance
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int alloc_pm_buffer(struct snd_emu10k1 *emu);
static void free_pm_buffer(struct snd_emu10k1 *emu);
#endif
snd_dma_free_pages(&emu->ptb_pages);
vfree(emu->page_ptr_table);
vfree(emu->page_addr_table);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
free_pm_buffer(emu);
#endif
if (emu->port)
err = snd_emu10k1_init(emu, enable_ir, 0);
if (err < 0)
goto error;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
err = alloc_pm_buffer(emu);
if (err < 0)
goto error;
return err;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static unsigned char saved_regs[] = {
CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
return IRQ_HANDLED;
}
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
int capture = 0;
switch(device) {
case 0:
strcpy(pcm->name, "EMU10K1X Front");
+ map = snd_pcm_std_chmaps;
break;
case 1:
strcpy(pcm->name, "EMU10K1X Rear");
+ map = surround_map;
break;
case 2:
strcpy(pcm->name, "EMU10K1X Center/LFE");
+ map = clfe_map;
break;
}
emu->pcm = pcm;
snd_dma_pci_data(emu->pci),
32*1024, 32*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int __devinit snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
{
int len;
runtime->hw.channels_min =
runtime->hw.channels_max = 16;
break;
- };
+ }
#endif
#if 0
/* For 96kHz */
spin_lock_irqsave(&emu->memblk_lock, flags);
if (blk->mapped_page >= 0) {
/* update order link */
- list_del(&blk->mapped_order_link);
- list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+ list_move_tail(&blk->mapped_order_link,
+ &emu->mapped_order_link_head);
spin_unlock_irqrestore(&emu->memblk_lock, flags);
return 0;
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_CHS 1 /* up to 4, but only first channel is used */
#ifdef CHIP1370
#define DRIVER_NAME "ENS1370"
+#define CHIP_NAME "ES1370" /* it can be ENS but just to keep compatibility... */
#else
#define DRIVER_NAME "ENS1371"
+#define CHIP_NAME "ES1371"
#endif
.pointer = snd_ensoniq_capture_pointer,
};
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
struct snd_pcm ** rpcm)
{
if (rpcm)
*rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC2/ADC");
-#else
- strcpy(pcm->name, "ES1371 DAC2/ADC");
-#endif
+ strcpy(pcm->name, CHIP_NAME " DAC2/ADC");
ensoniq->pcm1 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#endif
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
if (rpcm)
*rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm);
if (err < 0)
return err;
#endif
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC1");
-#else
- strcpy(pcm->name, "ES1371 DAC1");
-#endif
+ strcpy(pcm->name, CHIP_NAME " DAC1");
ensoniq->pcm2 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#endif
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
{
struct ensoniq *ensoniq = entry->private_data;
-#ifdef CHIP1370
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
-#else
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
-#endif
+ snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
snd_iprintf(buffer, "Joystick enable : %s\n",
ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
#ifdef CHIP1370
synchronize_irq(ensoniq->irq);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_ensoniq_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm
#else
#define SND_ENSONIQ_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int __devinit snd_ensoniq_create(struct snd_card *card,
struct pci_dev *pci,
*rrawmidi = NULL;
if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
return err;
-#ifdef CHIP1370
- strcpy(rmidi->name, "ES1370");
-#else
- strcpy(rmidi->name, "ES1371");
-#endif
+ strcpy(rmidi->name, CHIP_NAME);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned char saved_regs[SAVED_REG_SIZE];
#endif
};
outb(0, SLDM_REG(chip, DMACLEAR));
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* PM support
*/
#define ES1938_PM_OPS &es1938_pm
#else
#define ES1938_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef SUPPORT_JOYSTICK
static int __devinit snd_es1938_create_gameport(struct es1938 *chip)
/* linked list */
struct list_head list;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 wc_map[4];
#endif
};
struct list_head substream_list;
spinlock_t substream_lock;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 apu_map[NR_APUS][NR_APU_REGS];
#endif
{
if (snd_BUG_ON(channel >= NR_APUS))
return;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->apu_map[channel][reg] = data;
#endif
reg |= (channel << 4);
/* set the wavecache control reg */
wave_set_register(chip, es->apu[channel] << 3, tmpval);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
es->wc_map[channel] = tmpval;
#endif
}
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* PM support
*/
#define ES1968_PM_OPS &es1968_pm
#else
#define ES1968_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef SUPPORT_JOYSTICK
#define JOYSTICK_ADDR 0x200
struct snd_tea575x tea;
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 saved_regs[0x20];
#endif
};
snd_dma_pci_data(chip->pci),
chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps,
+ chip->multichannel ? 6 : 2, 0,
+ NULL);
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static unsigned char saved_regs[] = {
FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
#define SND_FM801_PM_OPS &snd_fm801_pm
#else
#define SND_FM801_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver fm801_driver = {
.name = KBUILD_MODNAME,
Say Y here to enable the generic HD-audio codec parser
in snd-hda-intel driver.
-config SND_HDA_POWER_SAVE
- bool "Aggressive power-saving on HD-audio"
- depends on PM
- help
- Say Y here to enable more aggressive power-saving mode on
- HD-audio driver. The power-saving timeout can be configured
- via power_save option or over sysfs on-the-fly.
-
config SND_HDA_POWER_SAVE_DEFAULT
int "Default time-out for HD-audio power-save mode"
- depends on SND_HDA_POWER_SAVE
+ depends on PM
default 0
help
The default time-out value in seconds for HD-audio automatic
memset(sequences_hp, 0, sizeof(sequences_hp));
assoc_line_out = 0;
- codec->ignore_misc_bit = true;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wid_caps = get_wcaps(codec, nid);
continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
- AC_DEFCFG_MISC_NO_PRESENCE))
- codec->ignore_misc_bit = false;
conn = get_defcfg_connect(def_conf);
if (conn == AC_JACK_PORT_NONE)
continue;
return channel_sfx[i];
}
+static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+
+ /* check the location */
+ switch (attr) {
+ case INPUT_PIN_ATTR_DOCK:
+ return "Dock ";
+ case INPUT_PIN_ATTR_FRONT:
+ return "Front ";
+ }
+ return "";
+}
+
+static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t *pins, int num_pins)
+{
+ int i, j, idx = 0;
+
+ const char *pfx = check_output_pfx(codec, nid);
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return -1;
+ for (j = 0; j < i; j++)
+ if (pfx == check_output_pfx(codec, pins[j]))
+ idx++;
+
+ return idx;
+}
+
static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
const struct auto_pin_cfg *cfg,
const char *name, char *label, int maxlen,
{
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int attr = snd_hda_get_input_pin_attr(def_conf);
- const char *pfx = "", *sfx = "";
+ const char *pfx, *sfx = "";
/* handle as a speaker if it's a fixed line-out */
if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
name = "Speaker";
- /* check the location */
- switch (attr) {
- case INPUT_PIN_ATTR_DOCK:
- pfx = "Dock ";
- break;
- case INPUT_PIN_ATTR_FRONT:
- pfx = "Front ";
- break;
- }
+ pfx = check_output_pfx(codec, nid);
+
if (cfg) {
/* try to give a unique suffix if needed */
sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
indexp);
if (!sfx) {
/* don't add channel suffix for Headphone controls */
- int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
- cfg->hp_outs);
+ int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
+ cfg->hp_outs);
if (idx >= 0)
*indexp = idx;
sfx = "";
for (q = quirk; q->subvendor; q++) {
unsigned int vendorid =
q->subdevice | (q->subvendor << 16);
- if (vendorid == codec->subsystem_id) {
+ unsigned int mask = 0xffff0000 | q->subdevice_mask;
+ if ((codec->subsystem_id & mask) == (vendorid & mask)) {
id = q->value;
#ifdef CONFIG_SND_DEBUG_VERBOSE
name = q->name;
}
EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static void hda_power_work(struct work_struct *work);
static void hda_keep_power_on(struct hda_codec *codec);
#define hda_codec_is_power_on(codec) ((codec)->power_on)
+static inline void hda_call_pm_notify(struct hda_bus *bus, bool power_up)
+{
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus, power_up);
+}
#else
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#define hda_codec_is_power_on(codec) 1
+#define hda_call_pm_notify(bus, state) {}
#endif
/**
{
struct hda_codec_preset_list *tbl;
const struct hda_codec_preset *preset;
- int mod_requested = 0;
+ unsigned int mod_requested = 0;
if (is_generic_config(codec))
return NULL; /* use the generic parser */
return;
snd_hda_jack_tbl_clear(codec);
restore_init_pincfgs(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
#endif
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+#ifdef CONFIG_PM
+ if (!codec->pm_down_notified) /* cancel leftover refcounts */
+ hda_call_pm_notify(codec->bus, false);
+#endif
module_put(codec->owner);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
hda_nid_t fg, unsigned int power_state);
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
/**
{
struct hda_codec *codec;
char component[31];
+ hda_nid_t fg;
int err;
if (snd_BUG_ON(!bus))
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spin_lock_init(&codec->power_lock);
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
* phase.
*/
hda_keep_power_on(codec);
+ hda_call_pm_notify(bus, true);
#endif
if (codec->bus->modelname) {
goto error;
}
- err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
+ fg = codec->afg ? codec->afg : codec->mfg;
+ err = read_widget_caps(codec, fg);
if (err < 0) {
snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
goto error;
goto error;
if (!codec->subsystem_id) {
- hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
codec->subsystem_id =
- snd_hda_codec_read(codec, nid, 0,
+ snd_hda_codec_read(codec, fg, 0,
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
- codec->epss = snd_hda_codec_get_supported_ps(codec,
- codec->afg ? codec->afg : codec->mfg,
+#ifdef CONFIG_PM
+ codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg,
+ AC_PWRST_CLKSTOP);
+ if (!codec->d3_stop_clk)
+ bus->power_keep_link_on = 1;
+#endif
+ codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
AC_PWRST_EPSS);
/* power-up all before initialization */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
+ hda_set_power_state(codec, AC_PWRST_D0);
snd_hda_codec_proc_new(codec);
/* OK, let it free */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
cancel_delayed_work_sync(&codec->power_work);
codec->power_on = 0;
codec->power_transition = 0;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
power_state);
}
-
- if (power_state == AC_PWRST_D0) {
- unsigned long end_time;
- int state;
- /* wait until the codec reachs to D0 */
- end_time = jiffies + msecs_to_jiffies(500);
- do {
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state == power_state)
- break;
- msleep(1);
- } while (time_after_eq(end_time, jiffies));
- }
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
}
/*
- * set power state of the codec
+ * wait until the state is reached, returns the current state
*/
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
+static unsigned int hda_sync_power_state(struct hda_codec *codec,
+ hda_nid_t fg,
+ unsigned int power_state)
{
- int count;
- unsigned int state;
+ unsigned long end_time = jiffies + msecs_to_jiffies(500);
+ unsigned int state, actual_state;
- if (codec->patch_ops.set_power_state) {
- codec->patch_ops.set_power_state(codec, fg, power_state);
- return;
+ for (;;) {
+ state = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (state & AC_PWRST_ERROR)
+ break;
+ actual_state = (state >> 4) & 0x0f;
+ if (actual_state == power_state)
+ break;
+ if (time_after_eq(jiffies, end_time))
+ break;
+ /* wait until the codec reachs to the target state */
+ msleep(1);
}
+ return state;
+}
+
+/*
+ * set power state of the codec, and return the power state
+ */
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state)
+{
+ hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;
+ int count;
+ unsigned int state;
/* this delay seems necessary to avoid click noise at power-down */
if (power_state == AC_PWRST_D3) {
/* repeat power states setting at most 10 times*/
for (count = 0; count < 10; count++) {
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- power_state);
- snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
+ if (codec->patch_ops.set_power_state)
+ codec->patch_ops.set_power_state(codec, fg,
+ power_state);
+ else {
+ snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_SET_POWER_STATE,
+ power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state,
+ true);
+ }
+ state = hda_sync_power_state(codec, fg, power_state);
if (!(state & AC_PWRST_ERROR))
break;
}
+
+ return state;
}
#ifdef CONFIG_SND_HDA_HWDEP
#ifdef CONFIG_PM
/*
* call suspend and power-down; used both from PM and power-save
+ * this function returns the power state in the end
*/
-static void hda_call_codec_suspend(struct hda_codec *codec)
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
{
+ unsigned int state;
+
if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec);
hda_cleanup_all_streams(codec);
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D3);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
+ state = hda_set_power_state(codec, AC_PWRST_D3);
+ /* Cancel delayed work if we aren't currently running from it. */
+ if (!in_wq)
+ cancel_delayed_work_sync(&codec->power_work);
spin_lock(&codec->power_lock);
snd_hda_update_power_acct(codec);
trace_hda_power_down(codec);
codec->power_transition = 0;
codec->power_jiffies = jiffies;
spin_unlock(&codec->power_lock);
-#endif
+ return state;
}
/*
* in the resume / power-save sequence
*/
hda_keep_power_on(codec);
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
+ hda_set_power_state(codec, AC_PWRST_D0);
restore_pincfgs(codec); /* restore all current pin configs */
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
}
+ snd_hda_jack_report_sync(codec);
snd_hda_power_down(codec); /* flag down before returning */
}
#endif /* CONFIG_PM */
}
EXPORT_SYMBOL_HDA(snd_hda_build_controls);
+/*
+ * add standard channel maps if not specified
+ */
+static int add_std_chmaps(struct hda_codec *codec)
+{
+ int i, str, err;
+
+ for (i = 0; i < codec->num_pcms; i++) {
+ for (str = 0; str < 2; str++) {
+ struct snd_pcm *pcm = codec->pcm_info[i].pcm;
+ struct hda_pcm_stream *hinfo =
+ &codec->pcm_info[i].stream[str];
+ struct snd_pcm_chmap *chmap;
+
+ if (codec->pcm_info[i].own_chmap)
+ continue;
+ if (!pcm || !hinfo->substreams)
+ continue;
+ err = snd_pcm_add_chmap_ctls(pcm, str,
+ snd_pcm_std_chmaps,
+ hinfo->channels_max,
+ 0, &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ }
+ }
+ return 0;
+}
+
int snd_hda_codec_build_controls(struct hda_codec *codec)
{
int err = 0;
err = codec->patch_ops.build_controls(codec);
if (err < 0)
return err;
+
+ /* we create chmaps here instead of build_pcms */
+ err = add_std_chmaps(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
return 0;
}
*
* This function returns 0 if successful, or a negative error code.
*/
-int __devinit snd_hda_build_pcms(struct hda_bus *bus)
+int snd_hda_build_pcms(struct hda_bus *bus)
{
struct hda_codec *codec;
}
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static void hda_power_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, power_work.work);
struct hda_bus *bus = codec->bus;
+ unsigned int state;
spin_lock(&codec->power_lock);
if (codec->power_transition > 0) { /* during power-up sequence? */
}
spin_unlock(&codec->power_lock);
- hda_call_codec_suspend(codec);
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ state = hda_call_codec_suspend(codec, true);
+ codec->pm_down_notified = 0;
+ if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) {
+ codec->pm_down_notified = 1;
+ hda_call_pm_notify(bus, false);
+ }
}
static void hda_keep_power_on(struct hda_codec *codec)
/* Transition to powered up, if wait_power_down then wait for a pending
* transition to D3 to complete. A pending D3 transition is indicated
* with power_transition == -1. */
+/* call this with codec->power_lock held! */
static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
{
struct hda_bus *bus = codec->bus;
- spin_lock(&codec->power_lock);
- codec->power_count++;
/* Return if power_on or transitioning to power_on, unless currently
* powering down. */
if ((codec->power_on || codec->power_transition > 0) &&
- !(wait_power_down && codec->power_transition < 0)) {
- spin_unlock(&codec->power_lock);
+ !(wait_power_down && codec->power_transition < 0))
return;
- }
spin_unlock(&codec->power_lock);
cancel_delayed_work_sync(&codec->power_work);
if (codec->power_on) {
if (codec->power_transition < 0)
codec->power_transition = 0;
- spin_unlock(&codec->power_lock);
return;
}
+
trace_hda_power_up(codec);
snd_hda_update_power_acct(codec);
codec->power_on = 1;
codec->power_transition = 1; /* avoid reentrance */
spin_unlock(&codec->power_lock);
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
+ if (codec->pm_down_notified) {
+ codec->pm_down_notified = 0;
+ hda_call_pm_notify(bus, true);
+ }
+
hda_call_codec_resume(codec);
spin_lock(&codec->power_lock);
codec->power_transition = 0;
- spin_unlock(&codec->power_lock);
-}
-
-/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
- */
-void snd_hda_power_up(struct hda_codec *codec)
-{
- __snd_hda_power_up(codec, false);
}
-EXPORT_SYMBOL_HDA(snd_hda_power_up);
-
-/**
- * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
- * D3 transition to complete. This differs from snd_hda_power_up() when
- * power_transition == -1. snd_hda_power_up sees this case as a nop,
- * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
- * back up.
- * @codec: HD-audio codec
- *
- * Cancel any power down operation hapenning on the work queue, then power up.
- */
-void snd_hda_power_up_d3wait(struct hda_codec *codec)
-{
- /* This will cancel and wait for pending power_work to complete. */
- __snd_hda_power_up(codec, true);
-}
-EXPORT_SYMBOL_HDA(snd_hda_power_up_d3wait);
#define power_save(codec) \
((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-/**
- * snd_hda_power_down - Power-down the codec
- * @codec: HD-audio codec
- *
- * Decrement the power-up counter and schedules the power-off work if
- * the counter rearches to zero.
- */
-void snd_hda_power_down(struct hda_codec *codec)
+/* Transition to powered down */
+static void __snd_hda_power_down(struct hda_codec *codec)
{
- spin_lock(&codec->power_lock);
- --codec->power_count;
- if (!codec->power_on || codec->power_count || codec->power_transition) {
- spin_unlock(&codec->power_lock);
+ if (!codec->power_on || codec->power_count || codec->power_transition)
return;
- }
+
if (power_save(codec)) {
codec->power_transition = -1; /* avoid reentrance */
queue_delayed_work(codec->bus->workq, &codec->power_work,
msecs_to_jiffies(power_save(codec) * 1000));
}
+}
+
+/**
+ * snd_hda_power_save - Power-up/down/sync the codec
+ * @codec: HD-audio codec
+ * @delta: the counter delta to change
+ *
+ * Change the power-up counter via @delta, and power up or down the hardware
+ * appropriately. For the power-down, queue to the delayed action.
+ * Passing zero to @delta means to synchronize the power state.
+ */
+void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait)
+{
+ spin_lock(&codec->power_lock);
+ codec->power_count += delta;
+ trace_hda_power_count(codec);
+ if (delta > 0)
+ __snd_hda_power_up(codec, d3wait);
+ else
+ __snd_hda_power_down(codec);
spin_unlock(&codec->power_lock);
}
-EXPORT_SYMBOL_HDA(snd_hda_power_down);
+EXPORT_SYMBOL_HDA(snd_hda_power_save);
/**
* snd_hda_check_amp_list_power - Check the amp list and update the power
list_for_each_entry(codec, &bus->codec_list, list) {
if (hda_codec_is_power_on(codec))
- hda_call_codec_suspend(codec);
+ hda_call_codec_suspend(codec, false);
}
return 0;
}
* @bus: the HDA bus
*
* Returns 0 if successful.
- *
- * This function is defined only when POWER_SAVE isn't set.
- * In the power-save mode, the codec is resumed dynamically.
*/
int snd_hda_resume(struct hda_bus *bus)
{
/* DIGITAL2 bits */
#define AC_DIG2_CC (0x7f<<0)
+/* DIGITAL3 bits */
+#define AC_DIG3_ICT (0xf<<0)
+#define AC_DIG3_KAE (1<<7)
+
/* Pin widget control - 8bit */
#define AC_PINCTL_EPT (0x3<<0)
#define AC_PINCTL_EPT_NATIVE 0
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_bus *bus);
+ void (*pm_notify)(struct hda_bus *bus, bool power_up);
#endif
};
#ifdef CONFIG_PM
int (*suspend)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */
+ bool own_chmap; /* codec driver provides own channel maps */
};
/* codec information */
unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
- unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
unsigned int pcm_format_first:1; /* PCM format must be set first */
unsigned int epss:1; /* supporting EPSS? */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */
+ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
+ unsigned int pm_down_notified:1; /* PM notified to controller */
int power_transition; /* power-state in transition */
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
#endif
/*
* power saving
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-void snd_hda_power_up(struct hda_codec *codec);
-void snd_hda_power_up_d3wait(struct hda_codec *codec);
-void snd_hda_power_down(struct hda_codec *codec);
+#ifdef CONFIG_PM
+void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
-static inline void snd_hda_power_up(struct hda_codec *codec) {}
-static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) {}
-static inline void snd_hda_power_down(struct hda_codec *codec) {}
+static inline void snd_hda_power_save(struct hda_codec *codec, int delta,
+ bool d3wait) {}
#endif
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */
+static inline void snd_hda_power_up(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 1, false);
+}
+
+/**
+ * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
+ * D3 transition to complete. This differs from snd_hda_power_up() when
+ * power_transition == -1. snd_hda_power_up sees this case as a nop,
+ * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
+ * back up.
+ * @codec: HD-audio codec
+ *
+ * Cancel any power down operation hapenning on the work queue, then power up.
+ */
+static inline void snd_hda_power_up_d3wait(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 1, true);
+}
+
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */
+static inline void snd_hda_power_down(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, -1, false);
+}
+
+/**
+ * snd_hda_power_sync - Synchronize the power-save status
+ * @codec: HD-audio codec
+ *
+ * Synchronize the actual power state with the power account;
+ * called when power_save parameter is changed
+ */
+static inline void snd_hda_power_sync(struct hda_codec *codec)
+{
+ snd_hda_power_save(codec, 0, false);
+}
+
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware
*/
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
+int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
/*
struct list_head nid_list; /* list of widgets */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
#define MAX_LOOPBACK_AMPS 7
struct hda_loopback_check loopback;
int num_loopbacks;
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx)
{
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_gspec *spec = codec->spec;
.build_controls = build_generic_controls,
.build_pcms = build_generic_pcms,
.free = snd_hda_generic_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
.check_power_status = generic_check_power_status,
#endif
};
#include <linux/mutex.h>
#include <linux/ctype.h>
#include <linux/string.h>
-#include <linux/firmware.h>
#include <linux/export.h>
#include <sound/core.h>
#include "hda_codec.h"
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static ssize_t power_on_acct_show(struct device *dev,
struct device_attribute *attr,
char *buf)
hwdep->device, &power_attrs[i]);
return 0;
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+#endif /* CONFIG_PM */
#ifdef CONFIG_SND_HDA_RECONFIG
*
* the spaces at the beginning and the end of the line are stripped
*/
-static int get_line_from_fw(char *buf, int size, struct firmware *fw)
+static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
+ const void **fw_data_p)
{
int len;
- const char *p = fw->data;
- while (isspace(*p) && fw->size) {
+ size_t fw_size = *fw_size_p;
+ const char *p = *fw_data_p;
+
+ while (isspace(*p) && fw_size) {
p++;
- fw->size--;
+ fw_size--;
}
- if (!fw->size)
+ if (!fw_size)
return 0;
- for (len = 0; len < fw->size; len++) {
+ for (len = 0; len < fw_size; len++) {
if (!*p)
break;
if (*p == '\n') {
*buf++ = *p++;
}
*buf = 0;
- fw->size -= len;
- fw->data = p;
+ *fw_size_p = fw_size - len;
+ *fw_data_p = p;
remove_trail_spaces(buf);
return 1;
}
/*
* load a "patch" firmware file and parse it
*/
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
+int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
{
- int err;
- const struct firmware *fw;
- struct firmware tmp;
char buf[128];
struct hda_codec *codec;
int line_mode;
- struct device *dev = bus->card->dev;
-
- if (snd_BUG_ON(!dev))
- return -ENODEV;
- err = request_firmware(&fw, patch, dev);
- if (err < 0) {
- printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
- patch);
- return err;
- }
- tmp = *fw;
line_mode = LINE_MODE_NONE;
codec = NULL;
- while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
+ while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
if (!*buf || *buf == '#' || *buf == '\n')
continue;
if (*buf == '[')
(codec || !patch_items[line_mode].need_codec))
patch_items[line_mode].parser(buf, bus, &codec);
}
- release_firmware(fw);
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_load_patch);
#include <linux/mutex.h>
#include <linux/reboot.h>
#include <linux/io.h>
+#include <linux/pm_runtime.h>
#ifdef CONFIG_X86
/* for snoop control */
#include <asm/pgtable.h>
#include <sound/initval.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
+#include <linux/firmware.h>
#include "hda_codec.h"
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static char *model[SNDRV_CARDS];
-static int position_fix[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
- "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
"(0=off, 1=on) (default=1).");
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
+static int param_set_xint(const char *val, const struct kernel_param *kp);
+static struct kernel_param_ops param_ops_xint = {
+ .set = param_set_xint,
+ .get = param_get_int,
+};
+#define param_check_xint param_check_int
+
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
+module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
-#endif
+#endif /* CONFIG_PM */
static int align_buffer_size = -1;
module_param(align_buffer_size, bint, 0644);
*/
unsigned int insufficient :1;
unsigned int wc_marked:1;
+ unsigned int no_period_wakeup:1;
};
/* CORB/RIRB */
struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ const struct firmware *fw;
+#endif
+
/* flags */
int position_fix[2]; /* for both playback/capture streams */
int poll_count;
/* reboot notifier (for mysterious hangup problem at power-down) */
struct notifier_block reboot_notifier;
+
+ /* card list (for power_save trigger) */
+ struct list_head list;
};
/* driver types */
#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
#define AZX_DCAPS_POSFIX_COMBO (1 << 24) /* Use COMBO as default */
+#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
* VGA-switcher support
*/
#ifdef SUPPORT_VGA_SWITCHEROO
+#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
+#else
+#define use_vga_switcheroo(chip) 0
+#endif
+
+#if defined(SUPPORT_VGA_SWITCHEROO) || defined(CONFIG_SND_HDA_PATCH_LOADER)
#define DELAYED_INIT_MARK
#define DELAYED_INITDATA_MARK
-#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
#else
#define DELAYED_INIT_MARK __devinit
#define DELAYED_INITDATA_MARK __devinitdata
-#define use_vga_switcheroo(chip) 0
#endif
static char *driver_short_names[] DELAYED_INITDATA_MARK = {
return azx_rirb_get_response(bus, addr);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_bus *bus);
+#ifdef CONFIG_PM
+static void azx_power_notify(struct hda_bus *bus, bool power_up);
#endif
/* reset codec link */
u8 sd_status;
int i, ok;
+#ifdef CONFIG_PM_RUNTIME
+ if (chip->pci->dev.power.runtime_status != RPM_ACTIVE)
+ return IRQ_NONE;
+#endif
+
spin_lock(&chip->reg_lock);
if (chip->disabled) {
ofs = 0;
azx_dev->frags = 0;
pos_adj = bdl_pos_adj[chip->dev_index];
- if (pos_adj > 0) {
+ if (!azx_dev->no_period_wakeup && pos_adj > 0) {
struct snd_pcm_runtime *runtime = substream->runtime;
int pos_align = pos_adj;
pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
pos_adj = 0;
} else {
ofs = setup_bdle(chip, substream, azx_dev,
- &bdl, ofs, pos_adj,
- !substream->runtime->no_period_wakeup);
+ &bdl, ofs, pos_adj, true);
if (ofs < 0)
goto error;
}
else
ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
period_bytes,
- !substream->runtime->no_period_wakeup);
+ !azx_dev->no_period_wakeup);
if (ofs < 0)
goto error;
}
bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
bus_temp.ops.bus_reset = azx_bus_reset;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify;
#endif
if (bufsize != azx_dev->bufsize ||
period_bytes != azx_dev->period_bytes ||
- format_val != azx_dev->format_val) {
+ format_val != azx_dev->format_val ||
+ runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
azx_dev->bufsize = bufsize;
azx_dev->period_bytes = period_bytes;
azx_dev->format_val = format_val;
+ azx_dev->no_period_wakeup = runtime->no_period_wakeup;
err = azx_setup_periods(chip, substream, azx_dev);
if (err < 0)
return err;
}
spin_lock(&chip->reg_lock);
- if (nsync > 1) {
- /* first, set SYNC bits of corresponding streams */
- if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
- azx_writel(chip, OLD_SSYNC,
- azx_readl(chip, OLD_SSYNC) | sbits);
- else
- azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits);
- }
+
+ /* first, set SYNC bits of corresponding streams */
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ azx_writel(chip, OLD_SSYNC,
+ azx_readl(chip, OLD_SSYNC) | sbits);
+ else
+ azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits);
+
snd_pcm_group_for_each_entry(s, substream) {
if (s->pcm->card != substream->pcm->card)
continue;
}
spin_unlock(&chip->reg_lock);
if (start) {
- if (nsync == 1)
- return 0;
/* wait until all FIFOs get ready */
for (timeout = 5000; timeout; timeout--) {
nwait = 0;
cpu_relax();
}
}
- if (nsync > 1) {
- spin_lock(&chip->reg_lock);
- /* reset SYNC bits */
- if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
- azx_writel(chip, OLD_SSYNC,
- azx_readl(chip, OLD_SSYNC) & ~sbits);
- else
- azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
- spin_unlock(&chip->reg_lock);
- }
+ spin_lock(&chip->reg_lock);
+ /* reset SYNC bits */
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ azx_writel(chip, OLD_SSYNC,
+ azx_readl(chip, OLD_SSYNC) & ~sbits);
+ else
+ azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
+ spin_unlock(&chip->reg_lock);
return 0;
}
if (pos >= azx_dev->bufsize)
pos = 0;
+
+ /* calculate runtime delay from LPIB */
+ if (azx_dev->substream->runtime &&
+ chip->position_fix[stream] == POS_FIX_POSBUF &&
+ (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
+ unsigned int lpib_pos = azx_sd_readl(azx_dev, SD_LPIB);
+ int delay;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+ if (delay < 0)
+ delay += azx_dev->bufsize;
+ if (delay >= azx_dev->period_bytes) {
+ snd_printdd("delay %d > period_bytes %d\n",
+ delay, azx_dev->period_bytes);
+ delay = 0; /* something is wrong */
+ }
+ azx_dev->substream->runtime->delay =
+ bytes_to_frames(azx_dev->substream->runtime, delay);
+ }
return pos;
}
chip->initialized = 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* power-up/down the controller */
-static void azx_power_notify(struct hda_bus *bus)
+static void azx_power_notify(struct hda_bus *bus, bool power_up)
{
struct azx *chip = bus->private_data;
+
+ if (power_up)
+ pm_runtime_get_sync(&chip->pci->dev);
+ else
+ pm_runtime_put_sync(&chip->pci->dev);
+}
+
+static DEFINE_MUTEX(card_list_lock);
+static LIST_HEAD(card_list);
+
+static void azx_add_card_list(struct azx *chip)
+{
+ mutex_lock(&card_list_lock);
+ list_add(&chip->list, &card_list);
+ mutex_unlock(&card_list_lock);
+}
+
+static void azx_del_card_list(struct azx *chip)
+{
+ mutex_lock(&card_list_lock);
+ list_del_init(&chip->list);
+ mutex_unlock(&card_list_lock);
+}
+
+/* trigger power-save check at writing parameter */
+static int param_set_xint(const char *val, const struct kernel_param *kp)
+{
+ struct azx *chip;
struct hda_codec *c;
- int power_on = 0;
+ int prev = power_save;
+ int ret = param_set_int(val, kp);
- list_for_each_entry(c, &bus->codec_list, list) {
- if (c->power_on) {
- power_on = 1;
- break;
- }
+ if (ret || prev == power_save)
+ return ret;
+
+ mutex_lock(&card_list_lock);
+ list_for_each_entry(chip, &card_list, list) {
+ if (!chip->bus || chip->disabled)
+ continue;
+ list_for_each_entry(c, &chip->bus->codec_list, list)
+ snd_hda_power_sync(c);
}
- if (power_on)
- azx_init_chip(chip, 1);
- else if (chip->running && power_save_controller &&
- !bus->power_keep_link_on)
- azx_stop_chip(chip);
+ mutex_unlock(&card_list_lock);
+ return 0;
}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+#else
+#define azx_add_card_list(chip) /* NOP */
+#define azx_del_card_list(chip) /* NOP */
+#endif /* CONFIG_PM */
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO)
/*
* power management
*/
-
static int azx_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume);
+#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
+
+#ifdef CONFIG_PM_RUNTIME
+static int azx_runtime_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+
+ if (!power_save_controller)
+ return -EAGAIN;
+
+ azx_stop_chip(chip);
+ azx_clear_irq_pending(chip);
+ return 0;
+}
+
+static int azx_runtime_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+
+ azx_init_pci(chip);
+ azx_init_chip(chip, 1);
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops azx_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+ SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, NULL)
+};
+
#define AZX_PM_OPS &azx_pm
#else
-#define azx_suspend(dev)
-#define azx_resume(dev)
#define AZX_PM_OPS NULL
#endif /* CONFIG_PM */
{
int i;
+ azx_del_card_list(chip);
+
azx_notifier_unregister(chip);
if (use_vga_switcheroo(chip)) {
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip->azx_dev);
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (chip->fw)
+ release_firmware(chip->fw);
+#endif
kfree(chip);
return 0;
const struct snd_pci_quirk *q;
switch (fix) {
+ case POS_FIX_AUTO:
case POS_FIX_LPIB:
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
chip->dev_index = dev;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->list);
init_vga_switcheroo(chip);
chip->position_fix[0] = chip->position_fix[1] =
static void power_down_all_codecs(struct azx *chip)
{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* The codecs were powered up in snd_hda_codec_new().
* Now all initialization done, so turn them down if possible
*/
#endif
}
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/* callback from request_firmware_nowait() */
+static void azx_firmware_cb(const struct firmware *fw, void *context)
+{
+ struct snd_card *card = context;
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = chip->pci;
+
+ if (!fw) {
+ snd_printk(KERN_ERR SFX "Cannot load firmware, aborting\n");
+ goto error;
+ }
+
+ chip->fw = fw;
+ if (!chip->disabled) {
+ /* continue probing */
+ if (azx_probe_continue(chip))
+ goto error;
+ }
+ return; /* OK */
+
+ error:
+ snd_card_free(card);
+ pci_set_drvdata(pci, NULL);
+}
+#endif
+
static int __devinit azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct azx *chip;
+ bool probe_now;
int err;
if (dev >= SNDRV_CARDS)
return err;
}
- /* set this here since it's referred in snd_hda_load_patch() */
snd_card_set_dev(card, &pci->dev);
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
+ probe_now = !chip->disabled;
- if (!chip->disabled) {
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (patch[dev] && *patch[dev]) {
+ snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
+ patch[dev]);
+ err = request_firmware_nowait(THIS_MODULE, true, patch[dev],
+ &pci->dev, GFP_KERNEL, card,
+ azx_firmware_cb);
+ if (err < 0)
+ goto out_free;
+ probe_now = false; /* continued in azx_firmware_cb() */
+ }
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+ if (probe_now) {
err = azx_probe_continue(chip);
if (err < 0)
goto out_free;
pci_set_drvdata(pci, card);
+ if (pci_dev_run_wake(pci))
+ pm_runtime_put_noidle(&pci->dev);
+
dev++;
return 0;
if (err < 0)
goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
- if (patch[dev] && *patch[dev]) {
- snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
- patch[dev]);
- err = snd_hda_load_patch(chip->bus, patch[dev]);
+ if (chip->fw) {
+ err = snd_hda_load_patch(chip->bus, chip->fw->size,
+ chip->fw->data);
if (err < 0)
goto out_free;
+ release_firmware(chip->fw); /* no longer needed */
+ chip->fw = NULL;
}
#endif
if ((probe_only[dev] & 1) == 0) {
chip->running = 1;
power_down_all_codecs(chip);
azx_notifier_register(chip);
+ azx_add_card_list(chip);
return 0;
static void __devexit azx_remove(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
+
+ if (pci_dev_run_wake(pci))
+ pm_runtime_get_noresume(&pci->dev);
+
if (card)
snd_card_free(card);
pci_set_drvdata(pci, NULL);
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* PBG */
{ PCI_DEVICE(0x8086, 0x1d20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
/* Panther Point */
{ PCI_DEVICE(0x8086, 0x1e20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* Lynx Point */
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c21),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0c0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
- AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
+ { PCI_DEVICE(0x8086, 0x0d0c),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
+ /* 5 Series/3400 */
+ { PCI_DEVICE(0x8086, 0x3b56),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
return false;
if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
return false;
- if (!codec->ignore_misc_bit &&
- (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
- AC_DEFCFG_MISC_NO_PRESENCE))
+ if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+ AC_DEFCFG_MISC_NO_PRESENCE)
return false;
if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
return false;
/**
* snd_hda_jack_detect_enable - enable the jack-detection
*/
-int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
- unsigned char action)
+int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action,
+ hda_jack_callback cb)
{
struct hda_jack_tbl *jack = snd_hda_jack_tbl_new(codec, nid);
if (!jack)
jack->jack_detect = 1;
if (action)
jack->action = action;
+ if (cb)
+ jack->callback = cb;
return snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | jack->tag);
}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable_callback);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action)
+{
+ return snd_hda_jack_detect_enable_callback(codec, nid, action, NULL);
+}
EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
/**
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctls);
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct hda_jack_tbl *event;
+ int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f;
+
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (!event)
+ return;
+ event->jack_dirty = 1;
+
+ if (event->callback)
+ event->callback(codec, event);
+
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event);
+
#define __SOUND_HDA_JACK_H
struct auto_pin_cfg;
+struct hda_jack_tbl;
+
+typedef void (*hda_jack_callback) (struct hda_codec *, struct hda_jack_tbl *);
struct hda_jack_tbl {
hda_nid_t nid;
unsigned char action; /* event action (0 = none) */
unsigned char tag; /* unsol event tag */
unsigned int private_data; /* arbitrary data */
+ hda_jack_callback callback;
/* jack-detection stuff */
unsigned int pin_sense; /* cached pin-sense value */
unsigned int jack_detect:1; /* capable of jack-detection? */
int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
unsigned char action);
+int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char action,
+ hda_jack_callback cb);
+
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
void snd_hda_jack_report_sync(struct hda_codec *codec);
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
#endif /* __SOUND_HDA_JACK_H */
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
-#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
+#if defined(CONFIG_PM) && defined(CONFIG_SND_HDA_HWDEP)
int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
#else
static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
{
unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
+ unsigned char digi2 = digi1 >> 8;
+ unsigned char digi3 = digi1 >> 16;
+
snd_iprintf(buffer, " Digital:");
if (digi1 & AC_DIG1_ENABLE)
snd_iprintf(buffer, " Enabled");
snd_iprintf(buffer, " Pro");
if (digi1 & AC_DIG1_LEVEL)
snd_iprintf(buffer, " GenLevel");
+ if (digi3 & AC_DIG3_KAE)
+ snd_iprintf(buffer, " KAE");
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Digital category: 0x%x\n",
- (digi1 >> 8) & AC_DIG2_CC);
+ digi2 & AC_DIG2_CC);
+ snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
+ digi3 & AC_DIG3_ICT);
}
static const char *get_pwr_state(u32 state)
TP_printk("[%d]", __entry->card)
);
+#ifdef CONFIG_PM
DECLARE_EVENT_CLASS(hda_power,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec)
);
+TRACE_EVENT(hda_power_count,
+ TP_PROTO(struct hda_codec *codec),
+ TP_ARGS(codec),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, addr )
+ __field( int, power_count )
+ __field( int, power_on )
+ __field( int, power_transition )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (codec)->bus->card->number;
+ __entry->addr = (codec)->addr;
+ __entry->power_count = (codec)->power_count;
+ __entry->power_on = (codec)->power_on;
+ __entry->power_transition = (codec)->power_transition;
+ ),
+
+ TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d",
+ __entry->card, __entry->addr, __entry->power_count,
+ __entry->power_on, __entry->power_transition)
+);
+#endif /* CONFIG_PM */
+
TRACE_EVENT(hda_unsol_event,
TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex),
unsigned int analog_beep: 1; /* analog beep input present */
unsigned int avoid_init_slave_vol:1;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
struct hda_loopback_check loopback;
#endif
/* for virtual master */
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct ad198x_spec *spec = codec->spec;
.build_pcms = ad198x_build_pcms,
.init = ad198x_init,
.free = ad198x_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = ad198x_check_power_status,
-#endif
#ifdef CONFIG_PM
+ .check_power_status = ad198x_check_power_status,
.suspend = ad198x_suspend,
#endif
.reboot_notify = ad198x_shutup,
{}
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1986a_loopbacks[] = {
{ 0x13, HDA_OUTPUT, 0 }, /* Mic */
{ 0x14, HDA_OUTPUT, 0 }, /* Phone */
spec->mixers[0] = ad1986a_mixers;
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1986a_loopbacks;
#endif
spec->vmaster_nid = 0x1b;
{ } /* end */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1983_loopbacks[] = {
{ 0x12, HDA_OUTPUT, 0 }, /* Mic */
{ 0x13, HDA_OUTPUT, 0 }, /* Line */
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1983_init_verbs;
spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1983_loopbacks;
#endif
spec->vmaster_nid = 0x05;
{ } /* end */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1981_loopbacks[] = {
{ 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
{ 0x13, HDA_OUTPUT, 0 }, /* Line */
.num_items = 3,
.items = {
{ "Mic", 0x0 },
- { "Docking-Station", 0x1 },
+ { "Dock Mic", 0x1 },
{ "Mix", 0x2 },
},
};
*/
HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
/* FIXME: does this laptop have analog CD connection? */
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1981_init_verbs;
spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1981_loopbacks;
#endif
spec->vmaster_nid = 0x05;
snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1988_loopbacks[] = {
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
{ 0x20, HDA_INPUT, 1 }, /* Line */
codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
break;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1988_loopbacks;
#endif
spec->vmaster_nid = 0x04;
{ } /* end */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1884_loopbacks[] = {
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
{ 0x20, HDA_INPUT, 1 }, /* Mic */
static const char * const ad1884_slave_vols[] = {
"PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
- "Internal Mic", "Docking Mic", /* "Beep", */ "IEC958",
+ "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
NULL
};
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1884_init_verbs;
spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1884_loopbacks;
#endif
spec->vmaster_nid = 0x04;
{ "Mic", 0x0 },
{ "Internal Mic", 0x1 },
{ "Mix", 0x3 },
- { "Docking-Station", 0x4 },
+ { "Dock Mic", 0x4 },
},
};
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
{ } /* end */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1884a_loopbacks[] = {
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
{ 0x20, HDA_INPUT, 1 }, /* Mic */
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1884a_init_verbs;
spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1884a_loopbacks;
#endif
codec->patch_ops = ad198x_patch_ops;
{ } /* end */
};
+/* simple auto-mute control for AD1882 3-stack board */
+#define AD1882_HP_EVENT 0x01
+
+static void ad1882_3stack_automute(struct hda_codec *codec)
+{
+ bool mute = snd_hda_jack_detect(codec, 0x11);
+ snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ mute ? 0 : PIN_OUT);
+}
+
+static int ad1882_3stack_automute_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1882_3stack_automute(codec);
+ return 0;
+}
+
+static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ switch (res >> 26) {
+ case AD1882_HP_EVENT:
+ ad1882_3stack_automute(codec);
+ break;
+ }
+}
+
static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
{ } /* end */
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+static const struct hda_verb ad1882_3stack_automute_verbs[] = {
+ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
+ { } /* end */
+};
+
+#ifdef CONFIG_PM
static const struct hda_amp_list ad1882_loopbacks[] = {
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
{ 0x20, HDA_INPUT, 1 }, /* Mic */
enum {
AD1882_3STACK,
AD1882_6STACK,
+ AD1882_3STACK_AUTOMUTE,
AD1882_MODELS
};
static const char * const ad1882_models[AD1986A_MODELS] = {
[AD1882_3STACK] = "3stack",
[AD1882_6STACK] = "6stack",
+ [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
};
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1882_init_verbs;
spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->loopback.amplist = ad1882_loopbacks;
#endif
spec->vmaster_nid = 0x04;
switch (board_config) {
default:
case AD1882_3STACK:
+ case AD1882_3STACK_AUTOMUTE:
spec->num_mixers = 3;
spec->mixers[2] = ad1882_3stack_mixers;
spec->channel_mode = ad1882_modes;
spec->need_dac_fix = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
+ if (board_config != AD1882_3STACK) {
+ spec->init_verbs[spec->num_init_verbs++] =
+ ad1882_3stack_automute_verbs;
+ codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
+ codec->patch_ops.init = ad1882_3stack_automute_init;
+ }
break;
case AD1882_6STACK:
spec->num_mixers = 3;
*/
struct cs_spec {
- int board_config;
+ struct hda_gen_spec gen;
+
struct auto_pin_cfg autocfg;
struct hda_multi_out multiout;
struct snd_kcontrol *vmaster_sw;
CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
- CS420X_IMAC27_122,
- CS420X_APPLE,
+ CS420X_GPIO_13,
+ CS420X_GPIO_23,
+ CS420X_MBP101,
+ CS420X_MBP101_COEF,
CS420X_AUTO,
- CS420X_MODELS
+ /* aliases */
+ CS420X_IMAC27_122 = CS420X_GPIO_23,
+ CS420X_APPLE = CS420X_GPIO_13,
};
/* CS421x boards */
enum {
CS421X_CDB4210,
- CS421X_MODELS
+ CS421X_SENSE_B,
};
/* Vendor-specific processing widget */
{} /* terminator */
};
+static const struct hda_verb mbp101_init_verbs[] = {
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0002},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x100a},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0004},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x000f},
+ {}
+};
+
/* SPDIF setup */
static void init_digital(struct hda_codec *codec)
{
init_output(codec);
init_input(codec);
init_digital(codec);
- snd_hda_jack_report_sync(codec);
return 0;
}
return 0;
}
-static const char * const cs420x_models[CS420X_MODELS] = {
- [CS420X_MBP53] = "mbp53",
- [CS420X_MBP55] = "mbp55",
- [CS420X_IMAC27] = "imac27",
- [CS420X_IMAC27_122] = "imac27_122",
- [CS420X_APPLE] = "apple",
- [CS420X_AUTO] = "auto",
+static const struct hda_model_fixup cs420x_models[] = {
+ { .id = CS420X_MBP53, .name = "mbp53" },
+ { .id = CS420X_MBP55, .name = "mbp55" },
+ { .id = CS420X_IMAC27, .name = "imac27" },
+ { .id = CS420X_IMAC27_122, .name = "imac27_122" },
+ { .id = CS420X_APPLE, .name = "apple" },
+ { .id = CS420X_MBP101, .name = "mbp101" },
+ {}
};
-
-static const struct snd_pci_quirk cs420x_cfg_tbl[] = {
+static const struct snd_pci_quirk cs420x_fixup_tbl[] = {
SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
/* this conflicts with too many other models */
/*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
- {} /* terminator */
-};
-static const struct snd_pci_quirk cs420x_codec_cfg_tbl[] = {
+ /* codec SSID */
SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
{} /* terminator */
};
-struct cs_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-static const struct cs_pincfg mbp53_pincfgs[] = {
+static const struct hda_pintbl mbp53_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100141 },
{ 0x0b, 0x90100140 },
{} /* terminator */
};
-static const struct cs_pincfg mbp55_pincfgs[] = {
+static const struct hda_pintbl mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 },
{ 0x0b, 0x90100120 },
{} /* terminator */
};
-static const struct cs_pincfg imac27_pincfgs[] = {
+static const struct hda_pintbl imac27_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100140 },
{ 0x0b, 0x90100142 },
{} /* terminator */
};
-static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
- [CS420X_MBP53] = mbp53_pincfgs,
- [CS420X_MBP55] = mbp55_pincfgs,
- [CS420X_IMAC27] = imac27_pincfgs,
+static const struct hda_pintbl mbp101_pincfgs[] = {
+ { 0x0d, 0x40ab90f0 },
+ { 0x0e, 0x90a600f0 },
+ { 0x12, 0x50a600f0 },
+ {} /* terminator */
};
-static void fix_pincfg(struct hda_codec *codec, int model,
- const struct cs_pincfg **pin_configs)
+static void cs420x_fixup_gpio_13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
{
- const struct cs_pincfg *cfg = pin_configs[model];
- if (!cfg)
- return;
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+ spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
}
+static void cs420x_fixup_gpio_23(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+ spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
+}
+
+static const struct hda_fixup cs420x_fixups[] = {
+ [CS420X_MBP53] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp53_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_APPLE,
+ },
+ [CS420X_MBP55] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp55_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_IMAC27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = imac27_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_GPIO_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_13,
+ },
+ [CS420X_GPIO_23] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_23,
+ },
+ [CS420X_MBP101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp101_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_MBP101_COEF,
+ },
+ [CS420X_MBP101_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = mbp101_init_verbs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+};
+
static int patch_cs420x(struct hda_codec *codec)
{
struct cs_spec *spec;
spec->vendor_nid = CS420X_VENDOR_NID;
- spec->board_config =
- snd_hda_check_board_config(codec, CS420X_MODELS,
- cs420x_models, cs420x_cfg_tbl);
- if (spec->board_config < 0)
- spec->board_config =
- snd_hda_check_board_codec_sid_config(codec,
- CS420X_MODELS, NULL, cs420x_codec_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config, cs_pincfgs);
-
- switch (spec->board_config) {
- case CS420X_IMAC27:
- case CS420X_MBP53:
- case CS420X_MBP55:
- case CS420X_APPLE:
- spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
- spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
- spec->gpio_mask = spec->gpio_dir =
- spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
- break;
- case CS420X_IMAC27_122:
- spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
- spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
- spec->gpio_mask = spec->gpio_dir =
- spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
- break;
- }
+ snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
+ cs420x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = cs_parse_auto_config(codec);
if (err < 0)
codec->patch_ops = cs_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
*/
/* CS4210 board names */
-static const char *cs421x_models[CS421X_MODELS] = {
- [CS421X_CDB4210] = "cdb4210",
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ {}
};
-static const struct snd_pci_quirk cs421x_cfg_tbl[] = {
+static const struct snd_pci_quirk cs421x_fixup_tbl[] = {
/* Test Intel board + CDB2410 */
SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
{} /* terminator */
/* CS4210 board pinconfigs */
/* Default CS4210 (CDB4210)*/
-static const struct cs_pincfg cdb4210_pincfgs[] = {
+static const struct hda_pintbl cdb4210_pincfgs[] = {
{ 0x05, 0x0321401f },
{ 0x06, 0x90170010 },
{ 0x07, 0x03813031 },
{} /* terminator */
};
-static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = {
- [CS421X_CDB4210] = cdb4210_pincfgs,
+/* Setup GPIO/SENSE for each board (if used) */
+static void cs421x_fixup_sense_b(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct cs_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
+}
+
+static const struct hda_fixup cs421x_fixups[] = {
+ [CS421X_CDB4210] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cdb4210_pincfgs,
+ .chained = true,
+ .chain_id = CS421X_SENSE_B,
+ },
+ [CS421X_SENSE_B] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs421x_fixup_sense_b,
+ }
};
static const struct hda_verb cs421x_coef_init_verbs[] = {
init_output(codec);
init_input(codec);
init_cs421x_digital(codec);
- snd_hda_jack_report_sync(codec);
return 0;
}
spec->vendor_nid = CS4210_VENDOR_NID;
- spec->board_config =
- snd_hda_check_board_config(codec, CS421X_MODELS,
- cs421x_models, cs421x_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config, cs421x_pincfgs);
- /*
- Setup GPIO/SENSE for each board (if used)
- */
- switch (spec->board_config) {
- case CS421X_CDB4210:
- snd_printd("CS4210 board: %s\n",
- cs421x_models[spec->board_config]);
-/* spec->gpio_mask = 3;
- spec->gpio_dir = 3;
- spec->gpio_data = 3;
-*/
- spec->sense_b = 1;
-
- break;
- }
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/*
Update the GPIO/DMIC/SENSE_B pinmux before the configuration
codec->patch_ops = cs421x_patch_ops;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
return 0;
error:
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int conexant_suspend(struct hda_codec *codec)
{
snd_hda_shutup_pins(codec);
.init = conexant_init,
.free = conexant_free,
.set_power_state = conexant_set_power,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
.suspend = conexant_suspend,
#endif
.reboot_notify = snd_hda_shutup_pins,
HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Switch", 0x15, 0x00, HDA_INPUT),
{}
};
do_automute(codec, cfg->line_outs, cfg->line_out_pins, on);
}
-static void cx_auto_hp_automute(struct hda_codec *codec)
+static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
cx_auto_update_speakers(codec);
}
-static void cx_auto_line_automute(struct hda_codec *codec)
+static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t pin, hda_nid_t *srcp,
bool do_select, int depth)
{
+ struct conexant_spec *spec = codec->spec;
hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
+ int startidx, i, nums;
switch (get_wcaps_type(get_wcaps(codec, mux))) {
case AC_WID_AUD_IN:
depth++;
if (depth == 2)
return -1;
+
+ /* Try to rotate around connections to avoid one boost controlling
+ another input path as well */
+ startidx = 0;
+ for (i = 0; i < spec->private_imux.num_items; i++)
+ if (spec->imux_info[i].pin == pin) {
+ startidx = i;
+ break;
+ }
+
for (i = 0; i < nums; i++) {
- int ret = __select_input_connection(codec, conn[i], pin, srcp,
+ int j = (i + startidx) % nums;
+ int ret = __select_input_connection(codec, conn[j], pin, srcp,
do_select, depth);
if (ret >= 0) {
if (do_select)
snd_hda_codec_write(codec, mux, 0,
- AC_VERB_SET_CONNECT_SEL, i);
- return i;
+ AC_VERB_SET_CONNECT_SEL, j);
+ return j;
}
}
return -1;
}
/* automatic switch internal and external mic */
-static void cx_auto_automic(struct hda_codec *codec)
+static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct conexant_spec *spec = codec->spec;
select_automic(codec, spec->auto_mic_int, false);
}
-static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (snd_hda_jack_get_action(codec, res >> 26)) {
- case CONEXANT_HP_EVENT:
- cx_auto_hp_automute(codec);
- break;
- case CONEXANT_LINE_EVENT:
- cx_auto_line_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cx_auto_automic(codec);
- break;
- }
- snd_hda_jack_report_sync(codec);
-}
-
/* check whether the pin config is suitable for auto-mic switching;
* auto-mic is enabled only when one int-mic and one ext- and/or
* one dock-mic exist
}
static void enable_unsol_pins(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, unsigned int action)
+ hda_nid_t *pins, unsigned int action,
+ hda_jack_callback cb)
{
int i;
for (i = 0; i < num_pins; i++)
- snd_hda_jack_detect_enable(codec, pins[i], action);
+ snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb);
}
static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
}
if (spec->auto_mute) {
enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins,
- CONEXANT_HP_EVENT);
+ CONEXANT_HP_EVENT, cx_auto_hp_automute);
spec->hp_present = detect_jacks(codec, cfg->hp_outs,
cfg->hp_pins);
if (spec->detect_line) {
enable_unsol_pins(codec, cfg->line_outs,
cfg->line_out_pins,
- CONEXANT_LINE_EVENT);
+ CONEXANT_LINE_EVENT,
+ cx_auto_line_automute);
spec->line_present =
detect_jacks(codec, cfg->line_outs,
cfg->line_out_pins);
if (spec->auto_mic) {
if (spec->auto_mic_ext >= 0) {
- snd_hda_jack_detect_enable(codec,
+ snd_hda_jack_detect_enable_callback(codec,
cfg->inputs[spec->auto_mic_ext].pin,
- CONEXANT_MIC_EVENT);
+ CONEXANT_MIC_EVENT, cx_auto_automic);
}
if (spec->auto_mic_dock >= 0) {
- snd_hda_jack_detect_enable(codec,
+ snd_hda_jack_detect_enable_callback(codec,
cfg->inputs[spec->auto_mic_dock].pin,
- CONEXANT_MIC_EVENT);
+ CONEXANT_MIC_EVENT, cx_auto_automic);
}
- cx_auto_automic(codec);
+ cx_auto_automic(codec, NULL);
} else {
select_input_connection(codec, spec->imux_info[0].adc,
spec->imux_info[0].pin);
cx_auto_init_output(codec);
cx_auto_init_input(codec);
cx_auto_init_digital(codec);
- snd_hda_jack_report_sync(codec);
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
return 0;
}
if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) {
spec->imux_info[idx].boost = mux;
- return cx_auto_add_volume(codec, label, " Boost", 0,
+ return cx_auto_add_volume(codec, label, " Boost", cidx,
mux, HDA_OUTPUT);
}
return 0;
.build_pcms = conexant_build_pcms,
.init = cx_auto_init,
.free = conexant_free,
- .unsol_event = cx_auto_unsol_event,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
.suspend = conexant_suspend,
#endif
.reboot_notify = snd_hda_shutup_pins,
SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
{}
};
#include <linux/module.h>
#include <sound/core.h>
#include <sound/jack.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_jack.h"
struct hdmi_eld sink_eld;
struct delayed_work work;
int repoll_count;
+ bool non_pcm;
+ bool chmap_set; /* channel-map override by ALSA API? */
+ unsigned char chmap[8]; /* ALSA API channel-map */
};
struct hdmi_spec {
int num_pins;
struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
struct hda_pcm pcm_rec[MAX_HDMI_PINS];
+ unsigned int channels_max; /* max over all cvts */
/*
* Non-generic ATI/NVIDIA specific
}
}
+static int get_channel_allocation_order(int ca)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channel_allocations[i].ca_index == ca)
+ break;
+ }
+ return i;
+}
+
/*
* The transformation takes two steps:
*
}
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid,
+ bool non_pcm,
int ca)
{
int i;
int err;
+ int order;
+ int non_pcm_mapping[8];
+
+ order = get_channel_allocation_order(ca);
if (hdmi_channel_mapping[ca][1] == 0) {
- for (i = 0; i < channel_allocations[ca].channels; i++)
+ for (i = 0; i < channel_allocations[order].channels; i++)
hdmi_channel_mapping[ca][i] = i | (i << 4);
for (; i < 8; i++)
hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
}
+ if (non_pcm) {
+ for (i = 0; i < channel_allocations[order].channels; i++)
+ non_pcm_mapping[i] = i | (i << 4);
+ for (; i < 8; i++)
+ non_pcm_mapping[i] = 0xf | (i << 4);
+ }
+
for (i = 0; i < 8; i++) {
err = snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
- hdmi_channel_mapping[ca][i]);
+ non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
if (err) {
snd_printdd(KERN_NOTICE
"HDMI: channel mapping failed\n");
hdmi_debug_channel_mapping(codec, pin_nid);
}
+struct channel_map_table {
+ unsigned char map; /* ALSA API channel map position */
+ unsigned char cea_slot; /* CEA slot value */
+ int spk_mask; /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+ { SNDRV_CHMAP_FL, 0x00, FL },
+ { SNDRV_CHMAP_FR, 0x01, FR },
+ { SNDRV_CHMAP_RL, 0x04, RL },
+ { SNDRV_CHMAP_RR, 0x05, RR },
+ { SNDRV_CHMAP_LFE, 0x02, LFE },
+ { SNDRV_CHMAP_FC, 0x03, FC },
+ { SNDRV_CHMAP_RLC, 0x06, RLC },
+ { SNDRV_CHMAP_RRC, 0x07, RRC },
+ {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+static int to_spk_mask(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->spk_mask;
+ }
+ return 0;
+}
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->cea_slot;
+ }
+ return 0x0f;
+}
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->cea_slot == c)
+ return t->map;
+ }
+ return 0;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+static int spk_to_chmap(int spk)
+{
+ struct channel_map_table *t = map_tables;
+ for (; t->map; t++) {
+ if (t->spk_mask == spk)
+ return t->map;
+ }
+ return 0;
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+ int i, spks = 0, spk_mask = 0;
+
+ for (i = 0; i < chs; i++) {
+ int mask = to_spk_mask(map[i]);
+ if (mask) {
+ spk_mask |= mask;
+ spks++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if ((chs == channel_allocations[i].channels ||
+ spks == channel_allocations[i].channels) &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask)
+ return channel_allocations[i].ca_index;
+ }
+ return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ int chs, unsigned char *map)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ int val, err;
+ if (i < chs)
+ val = to_cea_slot(map[i]);
+ else
+ val = 0xf;
+ val |= (i << 4);
+ err = snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT, val);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ if (i < channel_allocations[ca].channels)
+ map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f);
+ else
+ map[i] = 0;
+ }
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map)
+{
+ if (!non_pcm && map) {
+ hdmi_manual_setup_channel_mapping(codec, pin_nid,
+ channels, map);
+ } else {
+ hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
+ hdmi_setup_fake_chmap(map, ca);
+ }
+}
/*
* Audio InfoFrame routines
}
static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
- struct snd_pcm_substream *substream)
+ bool non_pcm,
+ struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
if (!eld->monitor_present)
return;
- ca = hdmi_channel_allocation(eld, channels);
+ if (!non_pcm && per_pin->chmap_set)
+ ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
+ else
+ ca = hdmi_channel_allocation(eld, channels);
+ if (ca < 0)
+ ca = 0;
memset(&ai, 0, sizeof(ai));
if (eld->conn_type == 0) { /* HDMI */
"pin=%d channels=%d\n",
pin_nid,
channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
+ hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+ channels, per_pin->chmap);
hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid,
ai.bytes, sizeof(ai));
hdmi_start_infoframe_trans(codec, pin_nid);
+ } else {
+ /* For non-pcm audio switch, setup new channel mapping
+ * accordingly */
+ if (per_pin->non_pcm != non_pcm)
+ hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+ channels, per_pin->chmap);
}
+
+ per_pin->non_pcm = non_pcm;
}
per_pin = &spec->pins[pin_idx];
per_pin->pin_nid = pin_nid;
+ per_pin->non_pcm = false;
err = hdmi_read_pin_conn(codec, pin_idx);
if (err < 0)
per_cvt->cvt_nid = cvt_nid;
per_cvt->channels_min = 2;
- if (chans <= 16)
+ if (chans <= 16) {
per_cvt->channels_max = chans;
+ if (chans > spec->channels_max)
+ spec->channels_max = chans;
+ }
err = snd_hda_query_supported_pcm(codec, cvt_nid,
&per_cvt->rates,
* can be lost and presence sense verb will become inaccurate if the
* HDA link is powered off at hot plug or hw initialization time.
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
AC_PWRST_EPSS))
codec->bus->power_keep_link_on = 1;
return &names[idx][0];
}
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hda_spdif_out *spdif;
+ bool non_pcm;
+
+ mutex_lock(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+ non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
+ mutex_unlock(&codec->spdif_mutex);
+ return non_pcm;
+}
+
+
/*
* HDMI callbacks
*/
int pin_idx = hinfo_to_pin_index(spec, hinfo);
hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
int pinctl;
+ bool non_pcm;
+
+ non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
- hdmi_setup_audio_infoframe(codec, pin_idx, substream);
+ hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream);
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
AC_VERB_SET_PIN_WIDGET_CONTROL,
pinctl & ~PIN_OUT);
snd_hda_spdif_ctls_unassign(codec, pin_idx);
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
}
+
return 0;
}
.cleanup = generic_hdmi_playback_pcm_cleanup,
};
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = spec->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ const unsigned int valid_mask =
+ FL | FR | RL | RR | LFE | FC | RLC | RRC;
+ unsigned int __user *dst;
+ int chs, count = 0;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (chs = 2; chs <= spec->channels_max; chs++) {
+ int i, c;
+ struct cea_channel_speaker_allocation *cap;
+ cap = channel_allocations;
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+ int chs_bytes = chs * 4;
+ if (cap->channels != chs)
+ continue;
+ if (cap->spk_mask & ~valid_mask)
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 7; c >= 0; c--) {
+ int spk = cap->speakers[c];
+ if (!spk)
+ continue;
+ if (put_user(spk_to_chmap(spk), dst))
+ return -EFAULT;
+ dst++;
+ }
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = kcontrol->private_value;
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
+ ucontrol->value.integer.value[i] = per_pin->chmap[i];
+ return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = info->private_data;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = kcontrol->private_value;
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ unsigned int ctl_idx;
+ struct snd_pcm_substream *substream;
+ unsigned char chmap[8];
+ int i, ca, prepared = 0;
+
+ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ substream = snd_pcm_chmap_substream(info, ctl_idx);
+ if (!substream || !substream->runtime)
+ return -EBADFD;
+ switch (substream->runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ prepared = 1;
+ break;
+ default:
+ return -EBUSY;
+ }
+ memset(chmap, 0, sizeof(chmap));
+ for (i = 0; i < ARRAY_SIZE(chmap); i++)
+ chmap[i] = ucontrol->value.integer.value[i];
+ if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
+ return 0;
+ ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+ if (ca < 0)
+ return -EINVAL;
+ per_pin->chmap_set = true;
+ memcpy(per_pin->chmap, chmap, sizeof(chmap));
+ if (prepared)
+ hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm,
+ substream);
+
+ return 0;
+}
+
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
info = &spec->pcm_rec[pin_idx];
info->name = get_hdmi_pcm_name(pin_idx);
info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->own_chmap = true;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
pstr->substreams = 1;
hdmi_present_sense(per_pin, 0);
}
+ /* add channel maps */
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int i;
+ err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 0, pin_idx, &chmap);
+ if (err < 0)
+ return err;
+ /* override handlers */
+ chmap->private_data = codec;
+ kctl = chmap->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ kctl->info = hdmi_chmap_ctl_info;
+ kctl->get = hdmi_chmap_ctl_get;
+ kctl->put = hdmi_chmap_ctl_put;
+ kctl->tlv.c = hdmi_chmap_ctl_tlv;
+ }
+
return 0;
}
hdmi_init_pin(codec, pin_nid);
snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
}
- snd_hda_jack_report_sync(codec);
return 0;
}
snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
snd_hda_jack_detect_enable(codec, pin, pin);
- snd_hda_jack_report_sync(codec);
return 0;
}
return 0;
}
+static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err = simple_playback_build_pcms(codec);
+ spec->pcm_rec[0].own_chmap = true;
+ return err;
+}
+
+static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_chmap *chmap;
+ int err;
+
+ err = simple_playback_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ /* add channel maps */
+ err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 8, 0, &chmap);
+ if (err < 0)
+ return err;
+ switch (codec->preset->id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ chmap->channel_mask = (1U << 2) | (1U << 8);
+ break;
+ case 0x10de0007:
+ chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
+ }
+ return 0;
+}
+
static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec->multiout.max_channels = 8;
spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
codec->patch_ops.init = nvhdmi_7x_init_8ch;
+ codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms;
+ codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls;
/* Initialize the audio infoframe channel mask and checksum to something
* valid */
/* hooks */
void (*init_hook)(struct hda_codec *codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
void (*power_hook)(struct hda_codec *codec);
#endif
void (*shutup)(struct hda_codec *codec);
/* for virtual master */
hda_nid_t vmaster_nid;
struct hda_vmaster_mute_hook vmaster_mute;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
struct hda_loopback_check loopback;
int num_loopbacks;
struct hda_amp_list loopback_list[8];
}
/* standard HP-automute helper */
-static void alc_hp_automute(struct hda_codec *codec)
+static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct alc_spec *spec = codec->spec;
}
/* standard line-out-automute helper */
-static void alc_line_automute(struct hda_codec *codec)
+static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct alc_spec *spec = codec->spec;
snd_hda_get_conn_index(codec, mux, nid, 0)
/* standard mic auto-switch helper */
-static void alc_mic_automute(struct hda_codec *codec)
+static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
struct alc_spec *spec = codec->spec;
hda_nid_t *pins = spec->imux_pins;
alc_mux_select(codec, 0, spec->int_mic_idx, false);
}
-/* handle the specified unsol action (ALC_XXX_EVENT) */
-static void alc_exec_unsol_event(struct hda_codec *codec, int action)
-{
- switch (action) {
- case ALC_HP_EVENT:
- alc_hp_automute(codec);
- break;
- case ALC_FRONT_EVENT:
- alc_line_automute(codec);
- break;
- case ALC_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
- snd_hda_jack_report_sync(codec);
-}
-
/* update the master volume per volume-knob's unsol event */
-static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid)
+static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
unsigned int val;
struct snd_kcontrol *kctl;
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return;
- val = snd_hda_codec_read(codec, nid, 0,
+ val = snd_hda_codec_read(codec, jack->nid, 0,
AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
val &= HDA_AMP_VOLMASK;
uctl->value.integer.value[0] = val;
kfree(uctl);
}
-/* unsolicited event for HP jack sensing */
-static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
{
- int action;
-
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- action = snd_hda_jack_get_action(codec, res);
- if (action == ALC_DCVOL_EVENT) {
- /* Execute the dc-vol event here as it requires the NID
- * but we don't pass NID to alc_exec_unsol_event().
- * Once when we convert all static quirks to the auto-parser,
- * this can be integerated into there.
- */
- struct hda_jack_tbl *jack;
- jack = snd_hda_jack_tbl_get_from_tag(codec, res);
- if (jack)
- alc_update_knob_master(codec, jack->nid);
- return;
- }
- alc_exec_unsol_event(codec, action);
+ /* For some reason, the res given from ALC880 is broken.
+ Here we adjust it properly. */
+ snd_hda_jack_unsol_event(codec, res >> 2);
}
/* call init functions of standard auto-mute helpers */
static void alc_inithook(struct hda_codec *codec)
{
- alc_hp_automute(codec);
- alc_line_automute(codec);
- alc_mic_automute(codec);
+ alc_hp_automute(codec, NULL);
+ alc_line_automute(codec, NULL);
+ alc_mic_automute(codec, NULL);
}
/* additional initialization for ALC888 variants */
continue;
snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
nid);
- snd_hda_jack_detect_enable(codec, nid, ALC_HP_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT,
+ alc_hp_automute);
spec->detect_hp = 1;
}
continue;
snd_printdd("realtek: Enable Line-Out "
"auto-muting on NID 0x%x\n", nid);
- snd_hda_jack_detect_enable(codec, nid,
- ALC_FRONT_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT,
+ alc_line_automute);
spec->detect_lo = 1;
- }
+ }
spec->automute_lo_possible = spec->detect_hp;
}
return false; /* no corresponding imux */
}
- snd_hda_jack_detect_enable(codec, spec->ext_mic_pin, ALC_MIC_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin,
+ ALC_MIC_EVENT, alc_mic_automute);
if (spec->dock_mic_pin)
- snd_hda_jack_detect_enable(codec, spec->dock_mic_pin,
- ALC_MIC_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin,
+ ALC_MIC_EVENT,
+ alc_mic_automute);
spec->auto_mic_valid_imux = 1;
spec->auto_mic = 1;
alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
- snd_hda_jack_report_sync(codec);
-
hda_call_check_power_status(codec, 0x01);
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct alc_spec *spec = codec->spec;
p = &alc_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
}
if (spec->adc_nids) {
p = spec->stream_analog_capture;
snd_hda_detach_beep_device(codec);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static void alc_power_eapd(struct hda_codec *codec)
{
alc_auto_setup_eapd(codec, false);
.build_pcms = alc_build_pcms,
.init = alc_init,
.free = alc_free,
- .unsol_event = alc_unsol_event,
+ .unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.resume = alc_resume,
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
.suspend = alc_suspend,
.check_power_status = alc_check_power_status,
#endif
.reboot_notify = alc_shutup,
};
+
/* replace the codec chip_name with the given string */
static int alc_codec_rename(struct hda_codec *codec, const char *name)
{
return channel_name[ch];
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
/* add the powersave loopback-list entry */
static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
{
const struct alc_fixup *fix, int action)
{
if (action == ALC_FIXUP_ACT_PROBE)
- snd_hda_jack_detect_enable(codec, 0x21, ALC_DCVOL_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master);
}
static const struct alc_fixup alc880_fixups[] = {
}
codec->patch_ops = alc_patch_ops;
+ codec->patch_ops.unsol_event = alc880_unsol_event;
+
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
spec->detect_hp = 1;
spec->automute_speaker = 1;
spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
- snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
+ snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT,
+ alc_hp_automute);
snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
}
}
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
spec = codec->spec;
+ alc_pick_fixup(codec, alc269_fixup_models,
+ alc269_fixup_tbl, alc269_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
if (codec->vendor_id == 0x10ec0269) {
spec->codec_variant = ALC269_TYPE_ALC269VA;
switch (alc_get_coef0(codec) & 0x00f0) {
alc269_fill_coef(codec);
}
- alc_pick_fixup(codec, alc269_fixup_models,
- alc269_fixup_tbl, alc269_fixups);
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
-
- alc_auto_parse_customize_define(codec);
-
/* automatic parse from the BIOS config */
err = alc269_parse_auto_config(codec);
if (err < 0)
}
codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
spec->power_hook = alc_power_eapd;
#endif
{ .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
{ .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
{ .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
+ { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
+ { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
STAC_HP_ZEPHYR,
STAC_92HD83XXX_HP_LED,
STAC_92HD83XXX_HP_INV_LED,
+ STAC_92HD83XXX_HP_MIC_LED,
STAC_92HD83XXX_MODELS
};
unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
unsigned int vref_led;
+ unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
+ bool mic_mute_led_on; /* current mic mute state */
+
/* stream */
unsigned int stream_delay;
[STAC_HP_ZEPHYR] = "hp-zephyr",
[STAC_92HD83XXX_HP_LED] = "hp-led",
[STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led",
+ [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led",
};
static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
+ "HP Folio", STAC_92HD83XXX_HP_MIC_LED),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
return knew;
}
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
- const struct snd_kcontrol_new *ktemp,
- int idx, const char *name,
- unsigned long val)
+static struct snd_kcontrol_new *
+add_control_temp(struct sigmatel_spec *spec,
+ const struct snd_kcontrol_new *ktemp,
+ int idx, const char *name,
+ unsigned long val)
{
struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
HDA_SUBDEV_AMP_FLAG);
if (!knew)
- return -ENOMEM;
+ return NULL;
knew->index = idx;
knew->private_value = val;
- return 0;
+ return knew;
+}
+
+static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
+ const struct snd_kcontrol_new *ktemp,
+ int idx, const char *name,
+ unsigned long val)
+{
+ return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM;
}
static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
idx = i;
break;
case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- idx = i;
- break;
+ if (num_outs <= 1) {
+ name = "Speaker";
+ idx = i;
+ break;
+ }
+ /* Fall through in case of multi speaker outs */
default:
name = chname[i];
idx = 0;
return 0;
}
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data);
+
+/* hook for controlling mic-mute LED GPIO */
+static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+ bool mute;
+
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err <= 0)
+ return err;
+ mute = !(ucontrol->value.integer.value[0] &&
+ ucontrol->value.integer.value[1]);
+ if (spec->mic_mute_led_on != mute) {
+ spec->mic_mute_led_on = mute;
+ if (mute)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ else
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ }
+ return err;
+}
+
static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
unsigned long sw, int idx)
{
+ struct sigmatel_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
int err;
+
err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
"Capture Volume", vol);
if (err < 0)
return err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
- "Capture Switch", sw);
- if (err < 0)
- return err;
+
+ knew = add_control_temp(spec,
+ &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE],
+ idx, "Capture Switch", sw);
+ if (!knew)
+ return -ENOMEM;
+ /* add a LED hook for some HP laptops */
+ if (spec->mic_mute_led_gpio)
+ knew->put = stac92xx_capture_sw_put_led;
+
return 0;
}
return 0;
}
+static void handle_unsol_event(struct hda_codec *codec,
+ struct hda_jack_tbl *event);
+
/* check if given nid is a valid pin and no other events are assigned
* to it. If OK, assign the event, set the unsol flag, and returns 1.
* Otherwise, returns zero.
if (event->action && event->action != type)
return 0;
event->action = type;
+ event->callback = handle_unsol_event;
snd_hda_jack_detect_enable(codec, nid, 0);
return 1;
}
stac_toggle_power_map(codec, nid, 0);
}
- snd_hda_jack_report_sync(codec);
-
/* sync mute LED */
if (spec->gpio_led) {
if (spec->vmaster_mute.hook)
handle_unsol_event(codec, event);
}
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct hda_jack_tbl *event;
- int tag;
-
- tag = (res >> 26) & 0x7f;
- event = snd_hda_jack_tbl_get_from_tag(codec, tag);
- if (!event)
- return;
- event->jack_dirty = 1;
- handle_unsol_event(codec, event);
- snd_hda_jack_report_sync(codec);
-}
-
static int hp_blike_system(u32 subsystem_id);
static void set_hp_led_gpio(struct hda_codec *codec)
.build_pcms = stac92xx_build_pcms,
.init = stac92xx_init,
.free = stac92xx_free,
- .unsol_event = stac92xx_unsol_event,
+ .unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.suspend = stac92xx_suspend,
.resume = stac92xx_resume,
case STAC_92HD83XXX_HP_INV_LED:
default_polarity = 1;
break;
+ case STAC_92HD83XXX_HP_MIC_LED:
+ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+ break;
}
if (find_mute_led_cfg(codec, default_polarity))
}
}
+ if (spec->mic_mute_led_gpio) {
+ spec->gpio_mask |= spec->mic_mute_led_gpio;
+ spec->gpio_dir |= spec->mic_mute_led_gpio;
+ spec->mic_mute_led_on = true;
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ }
+
err = stac92xx_parse_auto_config(codec);
if (!err) {
if (spec->board_config < 0) {
struct via_spec *spec = codec->spec;
if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
- (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
+ (spec->codec_type != VT1708 || spec->vt1708_jack_detect) &&
+ is_jack_detectable(codec, spec->autocfg.hp_pins[0]))
present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
if (spec->smart51_enabled)
}
#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
+#ifdef CONFIG_PM
static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
struct via_spec *spec = codec->spec;
.unsol_event = via_unsol_event,
#ifdef CONFIG_PM
.suspend = via_suspend,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
.check_power_status = via_check_power_status,
#endif
};
via_hp_automute(codec);
vt1708_update_hp_work(spec);
- snd_hda_jack_report_sync(codec);
return 0;
}
update_power_state(codec, 0x21, AC_PWRST_D3);
}
+/*
+ * pin fix-up
+ */
+enum {
+ VIA_FIXUP_INTMIC_BOOST,
+};
+
+static void via_fixup_intmic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ override_mic_boost(codec, 0x30, 0, 2, 40);
+}
+
+static const struct hda_fixup via_fixups[] = {
+ [VIA_FIXUP_INTMIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_intmic_boost,
+ },
+};
+
+static const struct snd_pci_quirk vt2002p_fixups[] = {
+ SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
+ {}
+};
+
/* patch for vt2002P */
static int patch_vt2002P(struct hda_codec *codec)
{
override_mic_boost(codec, 0x29, 0, 3, 40);
add_secret_dac_path(codec);
+ snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
if (err < 0) {
/*
* suspend/resume
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int aureon_resume(struct snd_ice1712 *ice)
{
struct aureon_spec *spec = ice->spec;
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = aureon_resume;
ice->pm_suspend_enabled = 1;
#endif
char **ext_clock_names;
int ext_clock_count;
void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(struct snd_ice1712 *);
unsigned int pm_suspend_enabled:1;
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_vt1724_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define SND_VT1724_PM_OPS &snd_vt1724_pm
#else
#define SND_VT1724_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static struct pci_driver vt1724_driver = {
.name = KBUILD_MODNAME,
* suspend/resume
* */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int juli_resume(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
ice->spdif.ops.open = juli_spdif_in_open;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = juli_resume;
ice->pm_suspend = juli_suspend;
ice->pm_suspend_enabled = 1;
ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int prodigy_hd2_resume(struct snd_ice1712 *ice)
{
/* initialize ak4396 codec and restore previous mixer volumes */
return -ENOMEM;
ice->spec = spec;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = &prodigy_hd2_resume;
ice->pm_suspend_enabled = 1;
#endif
snd_dma_pci_data(chip->pci),
rec->prealloc_size, rec->prealloc_max_size);
+ if (rec->ac97_idx == ICHD_PCMOUT && rec->playback_ops) {
+ struct snd_pcm_chmap *chmap;
+ int chs = 2;
+ if (rec->ac97_idx == ICHD_PCMOUT) {
+ if (chip->multi8)
+ chs = 8;
+ else if (chip->multi6)
+ chs = 6;
+ else if (chip->multi4)
+ chs = 4;
+ }
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chs, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+ }
+
return 0;
}
case DEVICE_INTEL_ICH4:
chip->spdif_idx = ICHD_SPBAR;
break;
- };
+ }
}
chip->in_ac97_init = 1;
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define INTEL8X0_PM_OPS &intel8x0_pm
#else
#define INTEL8X0_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define INTEL8X0M_PM_OPS &intel8x0m_pm
#else
#define INTEL8X0M_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PROC_FS
static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
#define K1212_ADAT_BUF_SIZE (K1212_ADAT_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers)
#define K1212_MAX_BUF_SIZE (K1212_ANALOG_BUF_SIZE + K1212_ADAT_BUF_SIZE)
-#define k1212MinADCSens 0x7f
-#define k1212MaxADCSens 0x00
+#define k1212MinADCSens 0x00
+#define k1212MaxADCSens 0x7f
#define k1212MaxVolume 0x7fff
#define k1212MaxWaveVolume 0xffff
#define k1212MinVolume 0x0000
unsigned int in_suspend;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u16 *suspend_mem;
#endif
outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
vfree(chip->suspend_mem);
#endif
/*
* APM support
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int m3_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define M3_PM_OPS &m3_pm
#else
#define M3_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_SND_MAESTRO3_INPUT
static int __devinit snd_m3_input_register(struct snd_m3 *chip)
}
chip->irq = pci->irq;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->suspend_mem = vmalloc(sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH));
if (chip->suspend_mem == NULL)
snd_printk(KERN_WARNING "can't allocate apm buffer\n");
if ((err = snd_card_register(chip->card)) < 0)
return err;
- };
+ }
snd_printdd("miXart firmware downloaded and successfully set up\n");
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* APM event handler, so the card is properly reinitialized after a power
* event.
#define NM256_PM_OPS &nm256_pm
#else
#define NM256_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int snd_nm256_free(struct nm256 *chip)
{
.id_table = oxygen_ids,
.probe = generic_oxygen_probe,
.remove = __devexit_p(oxygen_pci_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &oxygen_pci_pm,
},
)
);
void oxygen_pci_remove(struct pci_dev *pci);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
extern const struct dev_pm_ops oxygen_pci_pm;
#endif
void oxygen_pci_shutdown(struct pci_dev *pci);
}
EXPORT_SYMBOL(oxygen_pci_remove);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int oxygen_pci_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume);
EXPORT_SYMBOL(oxygen_pci_pm);
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
void oxygen_pci_shutdown(struct pci_dev *pci)
{
.id_table = xonar_ids,
.probe = xonar_probe,
.remove = __devexit_p(oxygen_pci_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &oxygen_pci_pm,
},
PCI_ID_PCX924E,
PCI_ID_PCX924HRMIC,
PCI_ID_PCX924E_MIC,
+ PCI_ID_VX442HR,
+ PCI_ID_PCX442HR,
+ PCI_ID_VX442E,
+ PCI_ID_PCX442E,
+ PCI_ID_VX822HR,
+ PCI_ID_PCX822HR,
+ PCI_ID_VX822E,
+ PCI_ID_PCX822E,
PCI_ID_LAST
};
{ 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
{ 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
{ 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
+ { 0x10b5, 0x9656, 0x1369, 0xd001, 0, 0, PCI_ID_VX442HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xd101, 0, 0, PCI_ID_PCX442HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xd021, 0, 0, PCI_ID_VX442E, },
+ { 0x10b5, 0x9056, 0x1369, 0xd121, 0, 0, PCI_ID_PCX442E, },
+ { 0x10b5, 0x9656, 0x1369, 0xd201, 0, 0, PCI_ID_VX822HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xd301, 0, 0, PCI_ID_PCX822HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xd221, 0, 0, PCI_ID_VX822E, },
+ { 0x10b5, 0x9056, 0x1369, 0xd321, 0, 0, PCI_ID_PCX822E, },
{ 0, }
};
[PCI_ID_PCX924E] = { "PCX924e", 1, 1, 5, 44 },
[PCI_ID_PCX924HRMIC] = { "PCX924HR-Mic", 1, 1, 5, 44 },
[PCI_ID_PCX924E_MIC] = { "PCX924e-Mic", 1, 1, 5, 44 },
+[PCI_ID_VX442HR] = { "VX442HR", 2, 2, 0, 41 },
+[PCI_ID_PCX442HR] = { "PCX442HR", 2, 2, 0, 41 },
+[PCI_ID_VX442E] = { "VX442e", 2, 2, 1, 41 },
+[PCI_ID_PCX442E] = { "PCX442e", 2, 2, 1, 41 },
+[PCI_ID_VX822HR] = { "VX822HR", 4, 1, 2, 42 },
+[PCI_ID_PCX822HR] = { "PCX822HR", 4, 1, 2, 42 },
+[PCI_ID_VX822E] = { "VX822e", 4, 1, 3, 42 },
+[PCI_ID_PCX822E] = { "PCX822e", 4, 1, 3, 42 },
};
/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- /* test 8 or 12 phys out */
- if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
+ /* test 4, 8 or 12 phys out */
+ if ((rmh.stat[0] & MASK_FIRST_FIELD) < mgr->playback_chips * 2)
return -EINVAL;
- /* test 8 or 2 phys in */
+ /* test 4, 8 or 2 phys in */
if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) <
mgr->capture_chips * 2)
return -EINVAL;
unsigned long received_irqs;
unsigned long handled_irqs;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int in_suspend;
#endif
};
}
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int riptide_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define RIPTIDE_PM_OPS &riptide_pm
#else
#define RIPTIDE_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
{
* we're not doing power management, we still need to allocate a page
* for the silence buffer.
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define SIS_SUSPEND_PAGES 4
#else
#define SIS_SUSPEND_PAGES 1
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int sis_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
#define SIS_PM_OPS &sis_pm
#else
#define SIS_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int sis_alloc_suspend(struct sis7019 *sis)
{
.id_table = snd_trident_ids,
.probe = snd_trident_probe,
.remove = __devexit_p(snd_trident_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_trident_pm,
},
}
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_trident_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
}
SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume);
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
unsigned char old_legacy;
unsigned char old_legacy_cfg;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
unsigned char legacy_saved;
unsigned char legacy_cfg_saved;
unsigned char spdif_ctrl_saved;
static int __devinit snd_via8233_pcm_new(struct via82xx *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
int i, err;
chip->playback_devno = 0; /* x 4 */
snd_dma_pci_data(chip->pci),
64*1024, VIA_MAX_BUFSIZE);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+
/* PCM #1: multi-channel playback and 2nd capture */
err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm);
if (err < 0)
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
64*1024, VIA_MAX_BUFSIZE);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 6, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+
return 0;
}
static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
int err;
chip->multi_devno = 0;
snd_dma_pci_data(chip->pci),
64*1024, VIA_MAX_BUFSIZE);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 6, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chip->ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+
/* SPDIF supported? */
if (! ac97_can_spdif(chip->ac97))
return 0;
if (mpu_port >= 0x200) { /* force MIDI */
mpu_port &= 0xfffc;
pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->mpu_port_saved = mpu_port;
#endif
} else {
snd_via686_create_gameport(chip, &legacy);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->legacy_saved = legacy;
chip->legacy_cfg_saved = legacy_cfg;
#endif
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define SND_VIA82XX_PM_OPS &snd_via82xx_pm
#else
#define SND_VIA82XX_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int snd_via82xx_free(struct via82xx *chip)
{
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* power management
*/
#define SND_VIA82XX_PM_OPS &snd_via82xx_pm
#else
#define SND_VIA82XX_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static int snd_via82xx_free(struct via82xx_modem *chip)
{
pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_vx222_suspend(struct device *dev)
{
struct pci_dev *pci = to_pci_dev(dev);
.id_table = snd_ymfpci_ids,
.probe = snd_card_ymfpci_probe,
.remove = __devexit_p(snd_card_ymfpci_remove),
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.driver = {
.pm = &snd_ymfpci_pm,
},
const struct firmware *dsp_microcode;
const struct firmware *controller_microcode;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
u32 *saved_regs;
u32 saved_ydsxgr_mode;
u16 saved_dsxg_legacy;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
.pointer = snd_ymfpci_playback_pointer,
};
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
{
struct snd_pcm *pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+ if (err < 0)
+ return err;
+
if (rpcm)
*rpcm = pcm;
return 0;
pci_set_power_state(chip->pci, 3);
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
vfree(chip->saved_regs);
#endif
if (chip->irq >= 0)
return snd_ymfpci_free(chip);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int saved_regs_index[] = {
/* spdif */
YDSXGR_SPDIFOUTCTRL,
}
SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
int __devinit snd_ymfpci_create(struct snd_card *card,
struct pci_dev * pci,
return err;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
if (chip->saved_regs == NULL) {
snd_ymfpci_free(chip);
select SND_JACK if INPUT=y || INPUT=SND
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
+ select SND_COMPRESS_OFFLOAD
---help---
If you want ASoC support, you should say Y here and also to the
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
source "sound/soc/blackfin/Kconfig"
+source "sound/soc/cirrus/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig"
-source "sound/soc/ep93xx/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-io.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o
snd-soc-dmaengine-pcm-objs := soc-dmaengine-pcm.o
obj-$(CONFIG_SND_SOC_DMAENGINE_PCM) += snd-soc-dmaengine-pcm.o
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += blackfin/
+obj-$(CONFIG_SND_SOC) += cirrus/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
-obj-$(CONFIG_SND_SOC) += ep93xx/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += mid-x86/
#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \
SND_SOC_DAIFMT_CBM_CFM)
-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 = "spi0.4",
- .ops = &bf5xx_ad1836_ops,
- .dai_fmt = BF5XX_AD1836_DAIFMT,
- },
- {
- .name = "ad1836",
- .stream_name = "AD1836",
- .cpu_dai_name = "bfin-tdm.1",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bfin-tdm-pcm-audio",
- .codec_name = "spi0.4",
- .ops = &bf5xx_ad1836_ops,
- .dai_fmt = BF5XX_AD1836_DAIFMT,
- },
+static struct snd_soc_dai_link bf5xx_ad1836_dai = {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .ops = &bf5xx_ad1836_ops,
+ .dai_fmt = BF5XX_AD1836_DAIFMT,
};
static struct snd_soc_card bf5xx_ad1836 = {
.name = "bfin-ad1836",
.owner = THIS_MODULE,
- .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
+ .dai_link = &bf5xx_ad1836_dai,
.num_links = 1,
};
-static struct platform_device *bfxx_ad1836_snd_device;
-
-static int __init bf5xx_ad1836_init(void)
+static __devinit int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
{
+ struct snd_soc_card *card = &bf5xx_ad1836;
+ const char **link_name;
int ret;
- bfxx_ad1836_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bfxx_ad1836_snd_device)
- return -ENOMEM;
+ link_name = pdev->dev.platform_data;
+ if (!link_name) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+ bf5xx_ad1836_dai.cpu_dai_name = link_name[0];
+ bf5xx_ad1836_dai.codec_name = link_name[1];
- platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836);
- ret = platform_device_add(bfxx_ad1836_snd_device);
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ ret = snd_soc_register_card(card);
if (ret)
- platform_device_put(bfxx_ad1836_snd_device);
-
+ dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
-static void __exit bf5xx_ad1836_exit(void)
+static int __devexit bf5xx_ad1836_driver_remove(struct platform_device *pdev)
{
- platform_device_unregister(bfxx_ad1836_snd_device);
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ return 0;
}
-module_init(bf5xx_ad1836_init);
-module_exit(bf5xx_ad1836_exit);
+static struct platform_driver bf5xx_ad1836_driver = {
+ .driver = {
+ .name = "bfin-snd-ad1836",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = bf5xx_ad1836_driver_probe,
+ .remove = __devexit_p(bf5xx_ad1836_driver_remove),
+};
+module_platform_driver(bf5xx_ad1836_driver);
/* Module information */
MODULE_AUTHOR("Barry Song");
--- /dev/null
+config SND_EP93XX_SOC
+ tristate "SoC Audio support for the Cirrus Logic EP93xx series"
+ depends on ARCH_EP93XX && SND_SOC
+ select SND_SOC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the EP93xx I2S or AC97 interfaces.
+
+config SND_EP93XX_SOC_I2S
+ tristate
+
+config SND_EP93XX_SOC_AC97
+ tristate
+ select AC97_BUS
+ select SND_SOC_AC97_BUS
+
+config SND_EP93XX_SOC_SNAPPERCL15
+ tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
+ depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
+ select SND_EP93XX_SOC_I2S
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y or M here if you want to add support for I2S audio on the
+ Bluewater Systems Snapper CL15 module.
+
+config SND_EP93XX_SOC_SIMONE
+ tristate "SoC Audio support for Simplemachines Sim.One board"
+ depends on SND_EP93XX_SOC && MACH_SIM_ONE
+ select SND_EP93XX_SOC_AC97
+ select SND_SOC_AC97_CODEC
+ help
+ Say Y or M here if you want to add support for AC97 audio on the
+ Simplemachines Sim.One board.
+
+config SND_EP93XX_SOC_EDB93XX
+ tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
+ depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
+ select SND_EP93XX_SOC_I2S
+ select SND_SOC_CS4271
+ help
+ Say Y or M here if you want to add support for I2S audio on the
+ Cirrus Logic EDB93xx boards.
--- /dev/null
+# EP93xx Platform Support
+snd-soc-ep93xx-objs := ep93xx-pcm.o
+snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
+snd-soc-ep93xx-ac97-objs := ep93xx-ac97.o
+
+obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
+obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
+obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
+
+# EP93XX Machine Support
+snd-soc-snappercl15-objs := snappercl15.o
+snd-soc-simone-objs := simone.o
+snd-soc-edb93xx-objs := edb93xx.o
+
+obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
+obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o
+obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o
--- /dev/null
+/*
+ * SoC audio for EDB93xx
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include "ep93xx-pcm.h"
+
+static int edb93xx_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->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int err;
+ unsigned int mclk_rate;
+ unsigned int rate = params_rate(params);
+
+ /*
+ * According to CS4271 datasheet we use MCLK/LRCK=256 for
+ * rates below 50kHz and 128 for higher sample rates
+ */
+ if (rate < 50000)
+ mclk_rate = rate * 64 * 4;
+ else
+ mclk_rate = rate * 64 * 2;
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
+ SND_SOC_CLOCK_IN);
+ if (err)
+ return err;
+
+ return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
+ SND_SOC_CLOCK_OUT);
+}
+
+static struct snd_soc_ops edb93xx_ops = {
+ .hw_params = edb93xx_hw_params,
+};
+
+static struct snd_soc_dai_link edb93xx_dai = {
+ .name = "CS4271",
+ .stream_name = "CS4271 HiFi",
+ .platform_name = "ep93xx-pcm-audio",
+ .cpu_dai_name = "ep93xx-i2s",
+ .codec_name = "spi0.0",
+ .codec_dai_name = "cs4271-hifi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &edb93xx_ops,
+};
+
+static struct snd_soc_card snd_soc_edb93xx = {
+ .name = "EDB93XX",
+ .owner = THIS_MODULE,
+ .dai_link = &edb93xx_dai,
+ .num_links = 1,
+};
+
+static int __devinit edb93xx_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_edb93xx;
+ int ret;
+
+ ret = ep93xx_i2s_acquire();
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ ep93xx_i2s_release();
+ }
+
+ return ret;
+}
+
+static int __devexit edb93xx_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ ep93xx_i2s_release();
+
+ return 0;
+}
+
+static struct platform_driver edb93xx_driver = {
+ .driver = {
+ .name = "edb93xx-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = edb93xx_probe,
+ .remove = __devexit_p(edb93xx_remove),
+};
+
+module_platform_driver(edb93xx_driver);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("ALSA SoC EDB93xx");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:edb93xx-audio");
--- /dev/null
+/*
+ * ASoC driver for Cirrus Logic EP93xx AC97 controller.
+ *
+ * Copyright (c) 2010 Mika Westerberg
+ *
+ * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+#include <linux/platform_data/dma-ep93xx.h>
+#include "ep93xx-pcm.h"
+
+/*
+ * Per channel (1-4) registers.
+ */
+#define AC97CH(n) (((n) - 1) * 0x20)
+
+#define AC97DR(n) (AC97CH(n) + 0x0000)
+
+#define AC97RXCR(n) (AC97CH(n) + 0x0004)
+#define AC97RXCR_REN BIT(0)
+#define AC97RXCR_RX3 BIT(3)
+#define AC97RXCR_RX4 BIT(4)
+#define AC97RXCR_CM BIT(15)
+
+#define AC97TXCR(n) (AC97CH(n) + 0x0008)
+#define AC97TXCR_TEN BIT(0)
+#define AC97TXCR_TX3 BIT(3)
+#define AC97TXCR_TX4 BIT(4)
+#define AC97TXCR_CM BIT(15)
+
+#define AC97SR(n) (AC97CH(n) + 0x000c)
+#define AC97SR_TXFE BIT(1)
+#define AC97SR_TXUE BIT(6)
+
+#define AC97RISR(n) (AC97CH(n) + 0x0010)
+#define AC97ISR(n) (AC97CH(n) + 0x0014)
+#define AC97IE(n) (AC97CH(n) + 0x0018)
+
+/*
+ * Global AC97 controller registers.
+ */
+#define AC97S1DATA 0x0080
+#define AC97S2DATA 0x0084
+#define AC97S12DATA 0x0088
+
+#define AC97RGIS 0x008c
+#define AC97GIS 0x0090
+#define AC97IM 0x0094
+/*
+ * Common bits for RGIS, GIS and IM registers.
+ */
+#define AC97_SLOT2RXVALID BIT(1)
+#define AC97_CODECREADY BIT(5)
+#define AC97_SLOT2TXCOMPLETE BIT(6)
+
+#define AC97EOI 0x0098
+#define AC97EOI_WINT BIT(0)
+#define AC97EOI_CODECREADY BIT(1)
+
+#define AC97GCR 0x009c
+#define AC97GCR_AC97IFE BIT(0)
+
+#define AC97RESET 0x00a0
+#define AC97RESET_TIMEDRESET BIT(0)
+
+#define AC97SYNC 0x00a4
+#define AC97SYNC_TIMEDSYNC BIT(0)
+
+#define AC97_TIMEOUT msecs_to_jiffies(5)
+
+/**
+ * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
+ * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
+ * @dev: pointer to the platform device dev structure
+ * @regs: mapped AC97 controller registers
+ * @done: bus ops wait here for an interrupt
+ */
+struct ep93xx_ac97_info {
+ struct mutex lock;
+ struct device *dev;
+ void __iomem *regs;
+ struct completion done;
+};
+
+/* currently ALSA only supports a single AC97 device */
+static struct ep93xx_ac97_info *ep93xx_ac97_info;
+
+static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_out = {
+ .name = "ac97-pcm-out",
+ .dma_port = EP93XX_DMA_AAC1,
+};
+
+static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_in = {
+ .name = "ac97-pcm-in",
+ .dma_port = EP93XX_DMA_AAC1,
+};
+
+static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
+ unsigned reg)
+{
+ return __raw_readl(info->regs + reg);
+}
+
+static inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
+ unsigned reg, unsigned val)
+{
+ __raw_writel(val, info->regs + reg);
+}
+
+static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ep93xx_ac97_info *info = ep93xx_ac97_info;
+ unsigned short val;
+
+ mutex_lock(&info->lock);
+
+ ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
+ ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
+ if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
+ dev_warn(info->dev, "timeout reading register %x\n", reg);
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+ val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
+
+ mutex_unlock(&info->lock);
+ return val;
+}
+
+static void ep93xx_ac97_write(struct snd_ac97 *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ struct ep93xx_ac97_info *info = ep93xx_ac97_info;
+
+ mutex_lock(&info->lock);
+
+ /*
+ * Writes to the codec need to be done so that slot 2 is filled in
+ * before slot 1.
+ */
+ ep93xx_ac97_write_reg(info, AC97S2DATA, val);
+ ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
+
+ ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
+ if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
+ dev_warn(info->dev, "timeout writing register %x\n", reg);
+
+ mutex_unlock(&info->lock);
+}
+
+static void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct ep93xx_ac97_info *info = ep93xx_ac97_info;
+
+ mutex_lock(&info->lock);
+
+ /*
+ * We are assuming that before this functions gets called, the codec
+ * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
+ * control the SYNC signal directly via AC97SYNC register. Using
+ * TIMEDSYNC the controller will keep the SYNC high > 1us.
+ */
+ ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
+ ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
+ if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
+ dev_warn(info->dev, "codec warm reset timeout\n");
+
+ mutex_unlock(&info->lock);
+}
+
+static void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+ struct ep93xx_ac97_info *info = ep93xx_ac97_info;
+
+ mutex_lock(&info->lock);
+
+ /*
+ * For doing cold reset, we disable the AC97 controller interface, clear
+ * WINT and CODECREADY bits, and finally enable the interface again.
+ */
+ ep93xx_ac97_write_reg(info, AC97GCR, 0);
+ ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
+ ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
+
+ /*
+ * Now, assert the reset and wait for the codec to become ready.
+ */
+ ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
+ ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
+ if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
+ dev_warn(info->dev, "codec cold reset timeout\n");
+
+ /*
+ * Give the codec some time to come fully out from the reset. This way
+ * we ensure that the subsequent reads/writes will work.
+ */
+ usleep_range(15000, 20000);
+
+ mutex_unlock(&info->lock);
+}
+
+static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
+{
+ struct ep93xx_ac97_info *info = dev_id;
+ unsigned status, mask;
+
+ /*
+ * Just mask out the interrupt and wake up the waiting thread.
+ * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
+ * the waiting thread.
+ */
+ status = ep93xx_ac97_read_reg(info, AC97GIS);
+ mask = ep93xx_ac97_read_reg(info, AC97IM);
+ mask &= ~status;
+ ep93xx_ac97_write_reg(info, AC97IM, mask);
+
+ complete(&info->done);
+ return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = ep93xx_ac97_read,
+ .write = ep93xx_ac97_write,
+ .reset = ep93xx_ac97_cold_reset,
+ .warm_reset = ep93xx_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
+ unsigned v = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * Enable compact mode, TX slots 3 & 4, and the TX FIFO
+ * itself.
+ */
+ v |= AC97TXCR_CM;
+ v |= AC97TXCR_TX3 | AC97TXCR_TX4;
+ v |= AC97TXCR_TEN;
+ ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
+ } else {
+ /*
+ * Enable compact mode, RX slots 3 & 4, and the RX FIFO
+ * itself.
+ */
+ v |= AC97RXCR_CM;
+ v |= AC97RXCR_RX3 | AC97RXCR_RX4;
+ v |= AC97RXCR_REN;
+ ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * As per Cirrus EP93xx errata described below:
+ *
+ * http://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
+ *
+ * we will wait for the TX FIFO to be empty before
+ * clearing the TEN bit.
+ */
+ unsigned long timeout = jiffies + AC97_TIMEOUT;
+
+ do {
+ v = ep93xx_ac97_read_reg(info, AC97SR(1));
+ if (time_after(jiffies, timeout)) {
+ dev_warn(info->dev, "TX timeout\n");
+ break;
+ }
+ } while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
+
+ /* disable the TX FIFO */
+ ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
+ } else {
+ /* disable the RX FIFO */
+ ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
+ }
+ break;
+
+ default:
+ dev_warn(info->dev, "unknown command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ep93xx_ac97_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ep93xx_pcm_dma_params *dma_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data = &ep93xx_ac97_pcm_out;
+ else
+ dma_data = &ep93xx_ac97_pcm_in;
+
+ snd_soc_dai_set_dma_data(dai, substream, dma_data);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
+ .startup = ep93xx_ac97_startup,
+ .trigger = ep93xx_ac97_trigger,
+};
+
+static struct snd_soc_dai_driver ep93xx_ac97_dai = {
+ .name = "ep93xx-ac97",
+ .id = 0,
+ .ac97_control = 1,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &ep93xx_ac97_dai_ops,
+};
+
+static int __devinit ep93xx_ac97_probe(struct platform_device *pdev)
+{
+ struct ep93xx_ac97_info *info;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ info->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->regs)
+ return -ENXIO;
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
+ IRQF_TRIGGER_HIGH, pdev->name, info);
+ if (ret)
+ goto fail;
+
+ dev_set_drvdata(&pdev->dev, info);
+
+ mutex_init(&info->lock);
+ init_completion(&info->done);
+ info->dev = &pdev->dev;
+
+ ep93xx_ac97_info = info;
+ platform_set_drvdata(pdev, info);
+
+ ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ platform_set_drvdata(pdev, NULL);
+ ep93xx_ac97_info = NULL;
+ dev_set_drvdata(&pdev->dev, NULL);
+ return ret;
+}
+
+static int __devexit ep93xx_ac97_remove(struct platform_device *pdev)
+{
+ struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+
+ /* disable the AC97 controller */
+ ep93xx_ac97_write_reg(info, AC97GCR, 0);
+
+ platform_set_drvdata(pdev, NULL);
+ ep93xx_ac97_info = NULL;
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_ac97_driver = {
+ .probe = ep93xx_ac97_probe,
+ .remove = __devexit_p(ep93xx_ac97_remove),
+ .driver = {
+ .name = "ep93xx-ac97",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ep93xx_ac97_driver);
+
+MODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-ac97");
--- /dev/null
+/*
+ * linux/sound/soc/ep93xx-i2s.c
+ * EP93xx I2S driver
+ *
+ * Copyright (C) 2010 Ryan Mallon
+ *
+ * Based on the original driver by:
+ * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/clk.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 <mach/hardware.h>
+#include <mach/ep93xx-regs.h>
+#include <linux/platform_data/dma-ep93xx.h>
+
+#include "ep93xx-pcm.h"
+
+#define EP93XX_I2S_TXCLKCFG 0x00
+#define EP93XX_I2S_RXCLKCFG 0x04
+#define EP93XX_I2S_GLCTRL 0x0C
+
+#define EP93XX_I2S_TXLINCTRLDATA 0x28
+#define EP93XX_I2S_TXCTRL 0x2C
+#define EP93XX_I2S_TXWRDLEN 0x30
+#define EP93XX_I2S_TX0EN 0x34
+
+#define EP93XX_I2S_RXLINCTRLDATA 0x58
+#define EP93XX_I2S_RXCTRL 0x5C
+#define EP93XX_I2S_RXWRDLEN 0x60
+#define EP93XX_I2S_RX0EN 0x64
+
+#define EP93XX_I2S_WRDLEN_16 (0 << 0)
+#define EP93XX_I2S_WRDLEN_24 (1 << 0)
+#define EP93XX_I2S_WRDLEN_32 (2 << 0)
+
+#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
+
+#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
+#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
+#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */
+#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
+#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
+
+struct ep93xx_i2s_info {
+ struct clk *mclk;
+ struct clk *sclk;
+ struct clk *lrclk;
+ struct ep93xx_pcm_dma_params *dma_params;
+ void __iomem *regs;
+};
+
+struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
+ [SNDRV_PCM_STREAM_PLAYBACK] = {
+ .name = "i2s-pcm-out",
+ .dma_port = EP93XX_DMA_I2S1,
+ },
+ [SNDRV_PCM_STREAM_CAPTURE] = {
+ .name = "i2s-pcm-in",
+ .dma_port = EP93XX_DMA_I2S1,
+ },
+};
+
+static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
+ unsigned reg, unsigned val)
+{
+ __raw_writel(val, info->regs + reg);
+}
+
+static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
+ unsigned reg)
+{
+ return __raw_readl(info->regs + reg);
+}
+
+static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
+{
+ unsigned base_reg;
+ int i;
+
+ if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
+ (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
+ /* Enable clocks */
+ clk_enable(info->mclk);
+ clk_enable(info->sclk);
+ clk_enable(info->lrclk);
+
+ /* Enable i2s */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
+ }
+
+ /* Enable fifos */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ base_reg = EP93XX_I2S_TX0EN;
+ else
+ base_reg = EP93XX_I2S_RX0EN;
+ for (i = 0; i < 3; i++)
+ ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
+}
+
+static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
+{
+ unsigned base_reg;
+ int i;
+
+ /* Disable fifos */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ base_reg = EP93XX_I2S_TX0EN;
+ else
+ base_reg = EP93XX_I2S_RX0EN;
+ for (i = 0; i < 3; i++)
+ ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
+
+ if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
+ (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
+ /* Disable i2s */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
+
+ /* Disable clocks */
+ clk_disable(info->lrclk);
+ clk_disable(info->sclk);
+ clk_disable(info->mclk);
+ }
+}
+
+static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream,
+ &info->dma_params[substream->stream]);
+ return 0;
+}
+
+static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ ep93xx_i2s_disable(info, substream->stream);
+}
+
+static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int clk_cfg, lin_ctrl;
+
+ clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
+ lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+ lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+ lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
+ break;
+
+ case SND_SOC_DAIFMT_RIGHT_J:
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+ lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* CPU is master */
+ clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Codec is master */
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Negative bit clock, lrclk low on left word */
+ clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Negative bit clock, lrclk low on right word */
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
+ clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Positive bit clock, lrclk low on left word */
+ clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
+ clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Positive bit clock, lrclk low on right word */
+ clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
+ break;
+ }
+
+ /* Write new register values */
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
+ return 0;
+}
+
+static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+ unsigned word_len, div, sdiv, lrdiv;
+ int err;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_len = EP93XX_I2S_WRDLEN_16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ word_len = EP93XX_I2S_WRDLEN_24;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ word_len = EP93XX_I2S_WRDLEN_32;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
+ else
+ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
+
+ /*
+ * EP93xx I2S module can be setup so SCLK / LRCLK value can be
+ * 32, 64, 128. MCLK / SCLK value can be 2 and 4.
+ * We set LRCLK equal to `rate' and minimum SCLK / LRCLK
+ * value is 64, because our sample size is 32 bit * 2 channels.
+ * I2S standard permits us to transmit more bits than
+ * the codec uses.
+ */
+ div = clk_get_rate(info->mclk) / params_rate(params);
+ sdiv = 4;
+ if (div > (256 + 512) / 2) {
+ lrdiv = 128;
+ } else {
+ lrdiv = 64;
+ if (div < (128 + 256) / 2)
+ sdiv = 2;
+ }
+
+ err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
+ if (err)
+ return err;
+
+ err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
+ if (err)
+ return err;
+
+ ep93xx_i2s_enable(info, substream->stream);
+ return 0;
+}
+
+static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
+ return -EINVAL;
+
+ return clk_set_rate(info->mclk, freq);
+}
+
+#ifdef CONFIG_PM
+static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ if (!dai->active)
+ return 0;
+
+ ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
+ ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+ return 0;
+}
+
+static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
+{
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ if (!dai->active)
+ return 0;
+
+ ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
+ ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+ return 0;
+}
+#else
+#define ep93xx_i2s_suspend NULL
+#define ep93xx_i2s_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+ .startup = ep93xx_i2s_startup,
+ .shutdown = ep93xx_i2s_shutdown,
+ .hw_params = ep93xx_i2s_hw_params,
+ .set_sysclk = ep93xx_i2s_set_sysclk,
+ .set_fmt = ep93xx_i2s_set_dai_fmt,
+};
+
+#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver ep93xx_i2s_dai = {
+ .symmetric_rates= 1,
+ .suspend = ep93xx_i2s_suspend,
+ .resume = ep93xx_i2s_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = EP93XX_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = EP93XX_I2S_FORMATS,
+ },
+ .ops = &ep93xx_i2s_dai_ops,
+};
+
+static int ep93xx_i2s_probe(struct platform_device *pdev)
+{
+ struct ep93xx_i2s_info *info;
+ struct resource *res;
+ int err;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ info->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!info->regs)
+ return -ENXIO;
+
+ info->mclk = clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(info->mclk)) {
+ err = PTR_ERR(info->mclk);
+ goto fail;
+ }
+
+ info->sclk = clk_get(&pdev->dev, "sclk");
+ if (IS_ERR(info->sclk)) {
+ err = PTR_ERR(info->sclk);
+ goto fail_put_mclk;
+ }
+
+ info->lrclk = clk_get(&pdev->dev, "lrclk");
+ if (IS_ERR(info->lrclk)) {
+ err = PTR_ERR(info->lrclk);
+ goto fail_put_sclk;
+ }
+
+ dev_set_drvdata(&pdev->dev, info);
+ info->dma_params = ep93xx_i2s_dma_params;
+
+ err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
+ if (err)
+ goto fail_put_lrclk;
+
+ return 0;
+
+fail_put_lrclk:
+ dev_set_drvdata(&pdev->dev, NULL);
+ clk_put(info->lrclk);
+fail_put_sclk:
+ clk_put(info->sclk);
+fail_put_mclk:
+ clk_put(info->mclk);
+fail:
+ return err;
+}
+
+static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
+{
+ struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ clk_put(info->lrclk);
+ clk_put(info->sclk);
+ clk_put(info->mclk);
+ return 0;
+}
+
+static struct platform_driver ep93xx_i2s_driver = {
+ .probe = ep93xx_i2s_probe,
+ .remove = __devexit_p(ep93xx_i2s_remove),
+ .driver = {
+ .name = "ep93xx-i2s",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ep93xx_i2s_driver);
+
+MODULE_ALIAS("platform:ep93xx-i2s");
+MODULE_AUTHOR("Ryan Mallon");
+MODULE_DESCRIPTION("EP93XX I2S driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
+ * Copyright (c) 2008 Ryan Mallon
+ *
+ * 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/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/platform_data/dma-ep93xx.h>
+#include <mach/hardware.h>
+#include <mach/ep93xx-regs.h>
+
+#include "ep93xx-pcm.h"
+
+static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = SNDRV_PCM_RATE_8000,
+ .rate_max = SNDRV_PCM_RATE_192000,
+
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+
+ .buffer_bytes_max = 131072,
+ .period_bytes_min = 32,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 32,
+};
+
+static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
+{
+ struct ep93xx_dma_data *data = filter_param;
+
+ if (data->direction == ep93xx_dma_chan_direction(chan)) {
+ chan->private = data;
+ return true;
+ }
+
+ return false;
+}
+
+static int ep93xx_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 ep93xx_pcm_dma_params *dma_params;
+ struct ep93xx_dma_data *dma_data;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
+
+ dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL);
+ if (!dma_data)
+ return -ENOMEM;
+
+ dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ dma_data->port = dma_params->dma_port;
+ dma_data->name = dma_params->name;
+ dma_data->direction = snd_pcm_substream_to_dma_direction(substream);
+
+ ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data);
+ if (ret) {
+ kfree(dma_data);
+ return ret;
+ }
+
+ snd_dmaengine_pcm_set_data(substream, dma_data);
+
+ return 0;
+}
+
+static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
+
+ snd_dmaengine_pcm_close(substream);
+ kfree(dma_data);
+ return 0;
+}
+
+static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops ep93xx_pcm_ops = {
+ .open = ep93xx_pcm_open,
+ .close = ep93xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = ep93xx_pcm_hw_params,
+ .hw_free = ep93xx_pcm_hw_free,
+ .trigger = snd_dmaengine_pcm_trigger,
+ .pointer = snd_dmaengine_pcm_pointer_no_residue,
+ .mmap = ep93xx_pcm_mmap,
+};
+
+static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ buf->bytes = size;
+
+ return (buf->area == NULL) ? -ENOMEM : 0;
+}
+
+static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
+ buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &ep93xx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver ep93xx_soc_platform = {
+ .ops = &ep93xx_pcm_ops,
+ .pcm_new = &ep93xx_pcm_new,
+ .pcm_free = &ep93xx_pcm_free_dma_buffers,
+};
+
+static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
+}
+
+static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver ep93xx_pcm_driver = {
+ .driver = {
+ .name = "ep93xx-pcm-audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = ep93xx_soc_platform_probe,
+ .remove = __devexit_p(ep93xx_soc_platform_remove),
+};
+
+module_platform_driver(ep93xx_pcm_driver);
+
+MODULE_AUTHOR("Ryan Mallon");
+MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-pcm-audio");
--- /dev/null
+/*
+ * sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * 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 _EP93XX_SND_SOC_PCM_H
+#define _EP93XX_SND_SOC_PCM_H
+
+struct ep93xx_pcm_dma_params {
+ char *name;
+ int dma_port;
+};
+
+#endif /* _EP93XX_SND_SOC_PCM_H */
--- /dev/null
+/*
+ * simone.c -- ASoC audio for Simplemachines Sim.One board
+ *
+ * Copyright (c) 2010 Mika Westerberg
+ *
+ * Based on snappercl15 machine driver by Ryan Mallon.
+ *
+ * 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/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "ep93xx-pcm.h"
+
+static struct snd_soc_dai_link simone_dai = {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "ep93xx-ac97",
+ .codec_dai_name = "ac97-hifi",
+ .codec_name = "ac97-codec",
+ .platform_name = "ep93xx-pcm-audio",
+};
+
+static struct snd_soc_card snd_soc_simone = {
+ .name = "Sim.One",
+ .owner = THIS_MODULE,
+ .dai_link = &simone_dai,
+ .num_links = 1,
+};
+
+static struct platform_device *simone_snd_ac97_device;
+
+static int __devinit simone_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_simone;
+ int ret;
+
+ simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
+ -1, NULL, 0);
+ if (IS_ERR(simone_snd_ac97_device))
+ return PTR_ERR(simone_snd_ac97_device);
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ platform_device_unregister(simone_snd_ac97_device);
+ }
+
+ return ret;
+}
+
+static int __devexit simone_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ platform_device_unregister(simone_snd_ac97_device);
+
+ return 0;
+}
+
+static struct platform_driver simone_driver = {
+ .driver = {
+ .name = "simone-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = simone_probe,
+ .remove = __devexit_p(simone_remove),
+};
+
+module_platform_driver(simone_driver);
+
+MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:simone-audio");
--- /dev/null
+/*
+ * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
+ *
+ * Copyright (C) 2008 Bluewater Systems Ltd
+ * Author: Ryan Mallon
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "../codecs/tlv320aic23.h"
+#include "ep93xx-pcm.h"
+
+#define CODEC_CLOCK 5644800
+
+static int snappercl15_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->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int err;
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
+ SND_SOC_CLOCK_IN);
+ if (err)
+ return err;
+
+ err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
+ SND_SOC_CLOCK_OUT);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static struct snd_soc_ops snappercl15_ops = {
+ .hw_params = snappercl15_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic Jack"},
+};
+
+static int snappercl15_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+ return 0;
+}
+
+static struct snd_soc_dai_link snappercl15_dai = {
+ .name = "tlv320aic23",
+ .stream_name = "AIC23",
+ .cpu_dai_name = "ep93xx-i2s",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .codec_name = "tlv320aic23-codec.0-001a",
+ .platform_name = "ep93xx-pcm-audio",
+ .init = snappercl15_tlv320aic23_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &snappercl15_ops,
+};
+
+static struct snd_soc_card snd_soc_snappercl15 = {
+ .name = "Snapper CL15",
+ .owner = THIS_MODULE,
+ .dai_link = &snappercl15_dai,
+ .num_links = 1,
+};
+
+static int __devinit snappercl15_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_snappercl15;
+ int ret;
+
+ ret = ep93xx_i2s_acquire();
+ if (ret)
+ return ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ ep93xx_i2s_release();
+ }
+
+ return ret;
+}
+
+static int __devexit snappercl15_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+ ep93xx_i2s_release();
+
+ return 0;
+}
+
+static struct platform_driver snappercl15_driver = {
+ .driver = {
+ .name = "snappercl15-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = snappercl15_probe,
+ .remove = __devexit_p(snappercl15_remove),
+};
+
+module_platform_driver(snappercl15_driver);
+
+MODULE_AUTHOR("Ryan Mallon");
+MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:snappercl15-audio");
select SND_SOC_CX20442
select SND_SOC_DA7210 if I2C
select SND_SOC_DA732X if I2C
+ select SND_SOC_DA9055 if I2C
select SND_SOC_DFBMCS320
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
+ select SND_SOC_WM0010 if SPI_MASTER
select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM2200 if I2C
config SND_SOC_DA732X
tristate
+config SND_SOC_DA9055
+ tristate
+
config SND_SOC_DFBMCS320
tristate
config SND_SOC_WL1273
tristate
+config SND_SOC_WM0010
+ tristate
+
config SND_SOC_WM1250_EV1
tristate
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da732x-objs := da732x.o
+snd-soc-da9055-objs := da9055.o
snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm0010-objs := wm0010.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
snd-soc-wm2200-objs := wm2200.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
+obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o
SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"),
/* Regulators */
- SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0),
- SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0),
- SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0),
- SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0),
/* Power */
SND_SOC_DAPM_SUPPLY("Audio Power",
dev_dbg(dev, "%s: Enter.\n", __func__);
/* Setup AB8500 according to board-settings */
- pdata = (struct ab8500_platform_data *)dev_get_platdata(dev->parent);
-
- /* Inform SoC Core that we have our own I/O arrangements. */
- codec->control_data = (void *)true;
+ pdata = dev_get_platdata(dev->parent);
if (np) {
if (!pdata)
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
#include "ad1836.h"
enum ad1836_type {
/* codec private data */
struct ad1836_priv {
enum ad1836_type type;
+ struct regmap *regmap;
};
/*
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(dai->codec);
int word_len = 0;
- struct snd_soc_codec *codec = dai->codec;
/* bit size */
switch (params_format(params)) {
break;
}
- snd_soc_update_bits(codec, AD1836_DAC_CTRL1, AD1836_DAC_WORD_LEN_MASK,
+ regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1,
+ AD1836_DAC_WORD_LEN_MASK,
word_len << AD1836_DAC_WORD_LEN_OFFSET);
- snd_soc_update_bits(codec, AD1836_ADC_CTRL2, AD1836_ADC_WORD_LEN_MASK,
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ AD1836_ADC_WORD_LEN_MASK,
word_len << AD1836_ADC_WORD_OFFSET);
return 0;
#ifdef CONFIG_PM
static int ad1836_suspend(struct snd_soc_codec *codec)
{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* reset clock control mode */
- return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
static int ad1836_resume(struct snd_soc_codec *codec)
{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* restore clock control mode */
- return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
}
#else
num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
- ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
- if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
- return ret;
- }
-
/* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */
- snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */
- snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0);
+ regmap_write(ad1836->regmap, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */
- snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
- snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL2, 0x180);
/* volume */
for (i = 1; i <= num_dacs; ++i) {
- snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF);
- snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF);
+ regmap_write(ad1836->regmap, AD1836_DAC_L_VOL(i), 0x3FF);
+ regmap_write(ad1836->regmap, AD1836_DAC_R_VOL(i), 0x3FF);
}
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
- snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
ret = snd_soc_add_codec_controls(codec, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
} else {
- snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00);
+ regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
}
ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
/* power down chip */
static int ad1836_remove(struct snd_soc_codec *codec)
{
+ struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
/* reset clock control mode */
- return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
.remove = ad1836_remove,
.suspend = ad1836_suspend,
.resume = ad1836_resume,
- .reg_cache_size = AD1836_NUM_REGS,
- .reg_word_size = sizeof(u16),
.controls = ad183x_controls,
.num_controls = ARRAY_SIZE(ad183x_controls),
.num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
};
+static const struct reg_default ad1836_reg_defaults[] = {
+ { AD1836_DAC_CTRL1, 0x0000 },
+ { AD1836_DAC_CTRL2, 0x0000 },
+ { AD1836_DAC_L_VOL(0), 0x0000 },
+ { AD1836_DAC_R_VOL(0), 0x0000 },
+ { AD1836_DAC_L_VOL(1), 0x0000 },
+ { AD1836_DAC_R_VOL(1), 0x0000 },
+ { AD1836_DAC_L_VOL(2), 0x0000 },
+ { AD1836_DAC_R_VOL(2), 0x0000 },
+ { AD1836_DAC_L_VOL(3), 0x0000 },
+ { AD1836_DAC_R_VOL(3), 0x0000 },
+ { AD1836_ADC_CTRL1, 0x0000 },
+ { AD1836_ADC_CTRL2, 0x0000 },
+ { AD1836_ADC_CTRL3, 0x0000 },
+};
+
+static const struct regmap_config ad1836_regmap_config = {
+ .val_bits = 12,
+ .reg_bits = 4,
+ .read_flag_mask = 0x08,
+
+ .max_register = AD1836_ADC_CTRL3,
+ .reg_defaults = ad1836_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
static int __devinit ad1836_spi_probe(struct spi_device *spi)
{
struct ad1836_priv *ad1836;
if (ad1836 == NULL)
return -ENOMEM;
+ ad1836->regmap = devm_regmap_init_spi(spi, &ad1836_regmap_config);
+ if (IS_ERR(ad1836->regmap))
+ return PTR_ERR(ad1836->regmap);
+
ad1836->type = spi_get_device_id(spi)->driver_data;
spi_set_drvdata(spi, ad1836);
.id_table = ad1836_ids,
};
-static int __init ad1836_init(void)
-{
- return spi_register_driver(&ad1836_spi_driver);
-}
-module_init(ad1836_init);
-
-static void __exit ad1836_exit(void)
-{
- spi_unregister_driver(&ad1836_spi_driver);
-}
-module_exit(ad1836_exit);
+module_spi_driver(ad1836_spi_driver);
MODULE_DESCRIPTION("ASoC ad1836 driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
static int __devinit ad193x_spi_probe(struct spi_device *spi)
{
struct ad193x_priv *ad193x;
- int ret;
ad193x = devm_kzalloc(&spi->dev, sizeof(struct ad193x_priv),
GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
- ad193x->regmap = regmap_init_spi(spi, &ad193x_spi_regmap_config);
- if (IS_ERR(ad193x->regmap)) {
- ret = PTR_ERR(ad193x->regmap);
- goto err_out;
- }
+ ad193x->regmap = devm_regmap_init_spi(spi, &ad193x_spi_regmap_config);
+ if (IS_ERR(ad193x->regmap))
+ return PTR_ERR(ad193x->regmap);
spi_set_drvdata(spi, ad193x);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad193x, &ad193x_dai, 1);
- if (ret < 0)
- goto err_regmap_exit;
-
- return 0;
-
-err_regmap_exit:
- regmap_exit(ad193x->regmap);
-err_out:
- return ret;
+ return snd_soc_register_codec(&spi->dev, &soc_codec_dev_ad193x,
+ &ad193x_dai, 1);
}
static int __devexit ad193x_spi_remove(struct spi_device *spi)
{
- struct ad193x_priv *ad193x = spi_get_drvdata(spi);
-
snd_soc_unregister_codec(&spi->dev);
- regmap_exit(ad193x->regmap);
return 0;
}
const struct i2c_device_id *id)
{
struct ad193x_priv *ad193x;
- int ret;
ad193x = devm_kzalloc(&client->dev, sizeof(struct ad193x_priv),
GFP_KERNEL);
if (ad193x == NULL)
return -ENOMEM;
- ad193x->regmap = regmap_init_i2c(client, &ad193x_i2c_regmap_config);
- if (IS_ERR(ad193x->regmap)) {
- ret = PTR_ERR(ad193x->regmap);
- goto err_out;
- }
+ ad193x->regmap = devm_regmap_init_i2c(client, &ad193x_i2c_regmap_config);
+ if (IS_ERR(ad193x->regmap))
+ return PTR_ERR(ad193x->regmap);
i2c_set_clientdata(client, ad193x);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ad193x, &ad193x_dai, 1);
- if (ret < 0)
- goto err_regmap_exit;
-
- return 0;
-
-err_regmap_exit:
- regmap_exit(ad193x->regmap);
-err_out:
- return ret;
+ return snd_soc_register_codec(&client->dev, &soc_codec_dev_ad193x,
+ &ad193x_dai, 1);
}
static int __devexit ad193x_i2c_remove(struct i2c_client *client)
{
- struct ad193x_priv *ad193x = i2c_get_clientdata(client);
-
snd_soc_unregister_codec(&client->dev);
- regmap_exit(ad193x->regmap);
return 0;
}
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
- codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
.id_table = adau1373_i2c_id,
};
-static int __init adau1373_init(void)
-{
- return i2c_add_driver(&adau1373_i2c_driver);
-}
-module_init(adau1373_init);
-
-static void __exit adau1373_exit(void)
-{
- i2c_del_driver(&adau1373_i2c_driver);
-}
-module_exit(adau1373_exit);
+module_i2c_driver(adau1373_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1373 driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
.id_table = adau1701_i2c_id,
};
-static int __init adau1701_init(void)
-{
- return i2c_add_driver(&adau1701_i2c_driver);
-}
-module_init(adau1701_init);
-
-static void __exit adau1701_exit(void)
-{
- i2c_del_driver(&adau1701_i2c_driver);
-}
-module_exit(adau1701_exit);
+module_i2c_driver(adau1701_i2c_driver);
MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
.id_table = ak4671_i2c_id,
};
-static int __init ak4671_modinit(void)
-{
- return i2c_add_driver(&ak4671_i2c_driver);
-}
-module_init(ak4671_modinit);
-
-static void __exit ak4671_exit(void)
-{
- i2c_del_driver(&ak4671_i2c_driver);
-}
-module_exit(ak4671_exit);
+module_i2c_driver(ak4671_i2c_driver);
MODULE_DESCRIPTION("ASoC AK4671 codec driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
"DSP1.4",
"DSP1.5",
"DSP1.6",
+ "DSP2.1",
+ "DSP2.2",
+ "DSP2.3",
+ "DSP2.4",
+ "DSP2.5",
+ "DSP2.6",
+ "DSP3.1",
+ "DSP3.2",
+ "DSP3.3",
+ "DSP3.4",
+ "DSP3.5",
+ "DSP3.6",
+ "DSP4.1",
+ "DSP4.2",
+ "DSP4.3",
+ "DSP4.4",
+ "DSP4.5",
+ "DSP4.6",
"ASRC1L",
"ASRC1R",
"ASRC2L",
0x6b,
0x6c,
0x6d,
+ 0x70, /* DSP2.1 */
+ 0x71,
+ 0x72,
+ 0x73,
+ 0x74,
+ 0x75,
+ 0x78, /* DSP3.1 */
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7c,
+ 0x7d,
+ 0x80, /* DSP4.1 */
+ 0x81,
+ 0x82,
+ 0x83,
+ 0x84,
+ 0x85,
0x90, /* ASRC1L */
0x91,
0x92,
}
EXPORT_SYMBOL_GPL(arizona_out_ev);
+static unsigned int arizona_sysclk_48k_rates[] = {
+ 6144000,
+ 12288000,
+ 22579200,
+ 49152000,
+ 73728000,
+ 98304000,
+ 147456000,
+};
+
+static unsigned int arizona_sysclk_44k1_rates[] = {
+ 5644800,
+ 11289600,
+ 24576000,
+ 45158400,
+ 67737600,
+ 90316800,
+ 135475200,
+};
+
+static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
+ unsigned int freq)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ unsigned int *rates;
+ int ref, div, refclk;
+
+ switch (clk) {
+ case ARIZONA_CLK_OPCLK:
+ reg = ARIZONA_OUTPUT_SYSTEM_CLOCK;
+ refclk = priv->sysclk;
+ break;
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ reg = ARIZONA_OUTPUT_ASYNC_CLOCK;
+ refclk = priv->asyncclk;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (refclk % 8000)
+ rates = arizona_sysclk_44k1_rates;
+ else
+ rates = arizona_sysclk_48k_rates;
+
+ for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
+ rates[ref] <= refclk; ref++) {
+ div = 1;
+ while (rates[ref] / div >= freq && div < 32) {
+ if (rates[ref] / div == freq) {
+ dev_dbg(codec->dev, "Configured %dHz OPCLK\n",
+ freq);
+ snd_soc_update_bits(codec, reg,
+ ARIZONA_OPCLK_DIV_MASK |
+ ARIZONA_OPCLK_SEL_MASK,
+ (div <<
+ ARIZONA_OPCLK_DIV_SHIFT) |
+ ref);
+ return 0;
+ }
+ div++;
+ }
+ }
+
+ dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+ return -EINVAL;
+}
+
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
reg = ARIZONA_ASYNC_CLOCK_1;
clk = &priv->asyncclk;
break;
+ case ARIZONA_CLK_OPCLK:
+ case ARIZONA_CLK_ASYNC_OPCLK:
+ return arizona_set_opclk(codec, clk_id, freq);
default:
return -EINVAL;
}
{
struct arizona_fll *fll = data;
- arizona_fll_dbg(fll, "Locked\n");
+ arizona_fll_dbg(fll, "Lock status changed\n");
complete(&fll->lock);
#include <sound/soc.h>
-#define ARIZONA_CLK_SYSCLK 1
-#define ARIZONA_CLK_ASYNCCLK 2
+#define ARIZONA_CLK_SYSCLK 1
+#define ARIZONA_CLK_ASYNCCLK 2
+#define ARIZONA_CLK_OPCLK 3
+#define ARIZONA_CLK_ASYNC_OPCLK 4
#define ARIZONA_CLK_SRC_MCLK1 0x0
#define ARIZONA_CLK_SRC_MCLK2 0x1
struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
};
-#define ARIZONA_NUM_MIXER_INPUTS 57
+#define ARIZONA_NUM_MIXER_INPUTS 75
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
/*
* The codec isn't really big-endian or little-endian, since the I2S
* This array contains the power-on default values of the registers, with the
* exception of the "CHIPID" register (01h). The lower four bits of that
* register contain the hardware revision, so it is treated as volatile.
- *
- * Also note that on the CS4270, the first readable register is 1, but ASoC
- * assumes the first register is 0. Therfore, the array must have an entry for
- * register 0, but we use cs4270_reg_is_readable() to tell ASoC that it can't
- * be read.
*/
-static const u8 cs4270_default_reg_cache[CS4270_LASTREG + 1] = {
- 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x20, 0x00, 0x00
+static const struct reg_default cs4270_reg_defaults[] = {
+ { 2, 0x00 },
+ { 3, 0x30 },
+ { 4, 0x00 },
+ { 5, 0x60 },
+ { 6, 0x20 },
+ { 7, 0x00 },
+ { 8, 0x00 },
};
static const char *supply_names[] = {
/* Private data for the CS4270 */
struct cs4270_private {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
unsigned int mclk; /* Input frequency of the MCLK pin */
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
-static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg)
+static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg)
{
return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
}
-static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg)
+static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
{
/* Unreadable registers are considered volatile */
if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
.name = "cs4270-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.rate_min = 4000,
static int cs4270_probe(struct snd_soc_codec *codec)
{
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
- int i, ret;
+ int ret;
/* Tell ASoC what kind of I/O to use to read the registers. ASoC will
* then do the I2C transactions itself.
*/
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs4270->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret);
return ret;
return ret;
}
- /* Add the non-DAPM controls */
- ret = snd_soc_add_codec_controls(codec, cs4270_snd_controls,
- ARRAY_SIZE(cs4270_snd_controls));
- if (ret < 0) {
- dev_err(codec->dev, "failed to add controls\n");
- return ret;
- }
-
- /* get the power supply regulators */
- for (i = 0; i < ARRAY_SIZE(supply_names); i++)
- cs4270->supplies[i].supply = supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
- if (ret < 0)
- return ret;
-
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
- if (ret < 0)
- goto error_free_regulators;
-
- return 0;
-
-error_free_regulators:
- regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
- cs4270->supplies);
return ret;
}
struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
- regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
return 0;
};
ndelay(500);
/* first restore the entire register cache ... */
- snd_soc_cache_sync(codec);
+ regcache_sync(cs4270->regmap);
/* ... then disable the power-down bits */
reg = snd_soc_read(codec, CS4270_PWRCTL);
.remove = cs4270_remove,
.suspend = cs4270_soc_suspend,
.resume = cs4270_soc_resume,
- .volatile_register = cs4270_reg_is_volatile,
- .readable_register = cs4270_reg_is_readable,
- .reg_cache_size = CS4270_LASTREG + 1,
- .reg_word_size = sizeof(u8),
- .reg_cache_default = cs4270_default_reg_cache,
+
+ .controls = cs4270_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4270_snd_controls),
+};
+
+/*
+ * cs4270_of_match - the device tree bindings
+ */
+static const struct of_device_id cs4270_of_match[] = {
+ { .compatible = "cirrus,cs4270", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4270_of_match);
+
+static const struct regmap_config cs4270_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = CS4270_LASTREG,
+ .reg_defaults = cs4270_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = cs4270_reg_is_readable,
+ .volatile_reg = cs4270_reg_is_volatile,
};
/**
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
+ struct device_node *np = i2c_client->dev.of_node;
struct cs4270_private *cs4270;
- int ret;
+ unsigned int val;
+ int ret, i;
- /* Verify that we have a CS4270 */
+ cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private),
+ GFP_KERNEL);
+ if (!cs4270) {
+ dev_err(&i2c_client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ /* get the power supply regulators */
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4270->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs4270->supplies),
+ cs4270->supplies);
+ if (ret < 0)
+ return ret;
+
+ /* See if we have a way to bring the codec out of reset */
+ if (np) {
+ enum of_gpio_flags flags;
+ int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
+
+ if (gpio_is_valid(gpio)) {
+ ret = devm_gpio_request_one(&i2c_client->dev, gpio,
+ flags & OF_GPIO_ACTIVE_LOW ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+ "cs4270 reset");
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap);
+ if (IS_ERR(cs4270->regmap))
+ return PTR_ERR(cs4270->regmap);
- ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
+ /* Verify that we have a CS4270 */
+ ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
i2c_client->addr);
return ret;
}
/* The top four bits of the chip ID should be 1100. */
- if ((ret & 0xF0) != 0xC0) {
+ if ((val & 0xF0) != 0xC0) {
dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
i2c_client->addr);
return -ENODEV;
dev_info(&i2c_client->dev, "found device at i2c address %X\n",
i2c_client->addr);
- dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF);
-
- cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private),
- GFP_KERNEL);
- if (!cs4270) {
- dev_err(&i2c_client->dev, "could not allocate codec\n");
- return -ENOMEM;
- }
+ dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF);
i2c_set_clientdata(i2c_client, cs4270);
- cs4270->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c_client->dev,
&soc_codec_device_cs4270, &cs4270_dai, 1);
.driver = {
.name = "cs4270",
.owner = THIS_MODULE,
+ .of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
-static int __init cs4270_init(void)
-{
- return i2c_add_driver(&cs4270_i2c_driver);
-}
-module_init(cs4270_init);
-
-static void __exit cs4270_exit(void)
-{
- i2c_del_driver(&cs4270_i2c_driver);
-}
-module_exit(cs4270_exit);
+module_i2c_driver(cs4270_i2c_driver);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
#include <sound/cs4271.h>
#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
#define cs4271_soc_resume NULL
#endif /* CONFIG_PM */
+#ifdef CONFIG_OF
+static const struct of_device_id cs4271_dt_ids[] = {
+ { .compatible = "cirrus,cs4271", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+#endif
+
static int cs4271_probe(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
int ret;
int gpio_nreset = -EINVAL;
+#ifdef CONFIG_OF
+ if (of_match_device(cs4271_dt_ids, codec->dev))
+ gpio_nreset = of_get_named_gpio(codec->dev->of_node,
+ "reset-gpio", 0);
+#endif
+
if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
gpio_nreset = cs4271plat->gpio_nreset;
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
.remove = __devexit_p(cs4271_spi_remove),
.driver = {
.name = "cs4271",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
},
.id_table = cs4271_i2c_id,
.probe = cs4271_i2c_probe,
.remove = cs42l51_i2c_remove,
};
-static int __init cs42l51_init(void)
-{
- int ret;
-
- ret = i2c_add_driver(&cs42l51_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
- return ret;
- }
- return 0;
-}
-module_init(cs42l51_init);
-
-static void __exit cs42l51_exit(void)
-{
- i2c_del_driver(&cs42l51_i2c_driver);
-}
-module_exit(cs42l51_exit);
+module_i2c_driver(cs42l51_i2c_driver);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
--- /dev/null
+/*
+ * DA9055 ALSA Soc codec driver
+ *
+ * Copyright (c) 2012 Dialog Semiconductor
+ *
+ * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
+ * Written by David Chen <david.chen@diasemi.com> and
+ * Ashish Chavan <ashish.chavan@kpitcummins.com>
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/da9055.h>
+
+/* DA9055 register space */
+
+/* Status Registers */
+#define DA9055_STATUS1 0x02
+#define DA9055_PLL_STATUS 0x03
+#define DA9055_AUX_L_GAIN_STATUS 0x04
+#define DA9055_AUX_R_GAIN_STATUS 0x05
+#define DA9055_MIC_L_GAIN_STATUS 0x06
+#define DA9055_MIC_R_GAIN_STATUS 0x07
+#define DA9055_MIXIN_L_GAIN_STATUS 0x08
+#define DA9055_MIXIN_R_GAIN_STATUS 0x09
+#define DA9055_ADC_L_GAIN_STATUS 0x0A
+#define DA9055_ADC_R_GAIN_STATUS 0x0B
+#define DA9055_DAC_L_GAIN_STATUS 0x0C
+#define DA9055_DAC_R_GAIN_STATUS 0x0D
+#define DA9055_HP_L_GAIN_STATUS 0x0E
+#define DA9055_HP_R_GAIN_STATUS 0x0F
+#define DA9055_LINE_GAIN_STATUS 0x10
+
+/* System Initialisation Registers */
+#define DA9055_CIF_CTRL 0x20
+#define DA9055_DIG_ROUTING_AIF 0X21
+#define DA9055_SR 0x22
+#define DA9055_REFERENCES 0x23
+#define DA9055_PLL_FRAC_TOP 0x24
+#define DA9055_PLL_FRAC_BOT 0x25
+#define DA9055_PLL_INTEGER 0x26
+#define DA9055_PLL_CTRL 0x27
+#define DA9055_AIF_CLK_MODE 0x28
+#define DA9055_AIF_CTRL 0x29
+#define DA9055_DIG_ROUTING_DAC 0x2A
+#define DA9055_ALC_CTRL1 0x2B
+
+/* Input - Gain, Select and Filter Registers */
+#define DA9055_AUX_L_GAIN 0x30
+#define DA9055_AUX_R_GAIN 0x31
+#define DA9055_MIXIN_L_SELECT 0x32
+#define DA9055_MIXIN_R_SELECT 0x33
+#define DA9055_MIXIN_L_GAIN 0x34
+#define DA9055_MIXIN_R_GAIN 0x35
+#define DA9055_ADC_L_GAIN 0x36
+#define DA9055_ADC_R_GAIN 0x37
+#define DA9055_ADC_FILTERS1 0x38
+#define DA9055_MIC_L_GAIN 0x39
+#define DA9055_MIC_R_GAIN 0x3A
+
+/* Output - Gain, Select and Filter Registers */
+#define DA9055_DAC_FILTERS5 0x40
+#define DA9055_DAC_FILTERS2 0x41
+#define DA9055_DAC_FILTERS3 0x42
+#define DA9055_DAC_FILTERS4 0x43
+#define DA9055_DAC_FILTERS1 0x44
+#define DA9055_DAC_L_GAIN 0x45
+#define DA9055_DAC_R_GAIN 0x46
+#define DA9055_CP_CTRL 0x47
+#define DA9055_HP_L_GAIN 0x48
+#define DA9055_HP_R_GAIN 0x49
+#define DA9055_LINE_GAIN 0x4A
+#define DA9055_MIXOUT_L_SELECT 0x4B
+#define DA9055_MIXOUT_R_SELECT 0x4C
+
+/* System Controller Registers */
+#define DA9055_SYSTEM_MODES_INPUT 0x50
+#define DA9055_SYSTEM_MODES_OUTPUT 0x51
+
+/* Control Registers */
+#define DA9055_AUX_L_CTRL 0x60
+#define DA9055_AUX_R_CTRL 0x61
+#define DA9055_MIC_BIAS_CTRL 0x62
+#define DA9055_MIC_L_CTRL 0x63
+#define DA9055_MIC_R_CTRL 0x64
+#define DA9055_MIXIN_L_CTRL 0x65
+#define DA9055_MIXIN_R_CTRL 0x66
+#define DA9055_ADC_L_CTRL 0x67
+#define DA9055_ADC_R_CTRL 0x68
+#define DA9055_DAC_L_CTRL 0x69
+#define DA9055_DAC_R_CTRL 0x6A
+#define DA9055_HP_L_CTRL 0x6B
+#define DA9055_HP_R_CTRL 0x6C
+#define DA9055_LINE_CTRL 0x6D
+#define DA9055_MIXOUT_L_CTRL 0x6E
+#define DA9055_MIXOUT_R_CTRL 0x6F
+
+/* Configuration Registers */
+#define DA9055_LDO_CTRL 0x90
+#define DA9055_IO_CTRL 0x91
+#define DA9055_GAIN_RAMP_CTRL 0x92
+#define DA9055_MIC_CONFIG 0x93
+#define DA9055_PC_COUNT 0x94
+#define DA9055_CP_VOL_THRESHOLD1 0x95
+#define DA9055_CP_DELAY 0x96
+#define DA9055_CP_DETECTOR 0x97
+#define DA9055_AIF_OFFSET 0x98
+#define DA9055_DIG_CTRL 0x99
+#define DA9055_ALC_CTRL2 0x9A
+#define DA9055_ALC_CTRL3 0x9B
+#define DA9055_ALC_NOISE 0x9C
+#define DA9055_ALC_TARGET_MIN 0x9D
+#define DA9055_ALC_TARGET_MAX 0x9E
+#define DA9055_ALC_GAIN_LIMITS 0x9F
+#define DA9055_ALC_ANA_GAIN_LIMITS 0xA0
+#define DA9055_ALC_ANTICLIP_CTRL 0xA1
+#define DA9055_ALC_ANTICLIP_LEVEL 0xA2
+#define DA9055_ALC_OFFSET_OP2M_L 0xA6
+#define DA9055_ALC_OFFSET_OP2U_L 0xA7
+#define DA9055_ALC_OFFSET_OP2M_R 0xAB
+#define DA9055_ALC_OFFSET_OP2U_R 0xAC
+#define DA9055_ALC_CIC_OP_LVL_CTRL 0xAD
+#define DA9055_ALC_CIC_OP_LVL_DATA 0xAE
+#define DA9055_DAC_NG_SETUP_TIME 0xAF
+#define DA9055_DAC_NG_OFF_THRESHOLD 0xB0
+#define DA9055_DAC_NG_ON_THRESHOLD 0xB1
+#define DA9055_DAC_NG_CTRL 0xB2
+
+/* SR bit fields */
+#define DA9055_SR_8000 (0x1 << 0)
+#define DA9055_SR_11025 (0x2 << 0)
+#define DA9055_SR_12000 (0x3 << 0)
+#define DA9055_SR_16000 (0x5 << 0)
+#define DA9055_SR_22050 (0x6 << 0)
+#define DA9055_SR_24000 (0x7 << 0)
+#define DA9055_SR_32000 (0x9 << 0)
+#define DA9055_SR_44100 (0xA << 0)
+#define DA9055_SR_48000 (0xB << 0)
+#define DA9055_SR_88200 (0xE << 0)
+#define DA9055_SR_96000 (0xF << 0)
+
+/* REFERENCES bit fields */
+#define DA9055_BIAS_EN (1 << 3)
+#define DA9055_VMID_EN (1 << 7)
+
+/* PLL_CTRL bit fields */
+#define DA9055_PLL_INDIV_10_20_MHZ (1 << 2)
+#define DA9055_PLL_SRM_EN (1 << 6)
+#define DA9055_PLL_EN (1 << 7)
+
+/* AIF_CLK_MODE bit fields */
+#define DA9055_AIF_BCLKS_PER_WCLK_32 (0 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_64 (1 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_128 (2 << 0)
+#define DA9055_AIF_BCLKS_PER_WCLK_256 (3 << 0)
+#define DA9055_AIF_CLK_EN_SLAVE_MODE (0 << 7)
+#define DA9055_AIF_CLK_EN_MASTER_MODE (1 << 7)
+
+/* AIF_CTRL bit fields */
+#define DA9055_AIF_FORMAT_I2S_MODE (0 << 0)
+#define DA9055_AIF_FORMAT_LEFT_J (1 << 0)
+#define DA9055_AIF_FORMAT_RIGHT_J (2 << 0)
+#define DA9055_AIF_WORD_S16_LE (0 << 2)
+#define DA9055_AIF_WORD_S20_3LE (1 << 2)
+#define DA9055_AIF_WORD_S24_LE (2 << 2)
+#define DA9055_AIF_WORD_S32_LE (3 << 2)
+
+/* MIXIN_L_CTRL bit fields */
+#define DA9055_MIXIN_L_MIX_EN (1 << 3)
+
+/* MIXIN_R_CTRL bit fields */
+#define DA9055_MIXIN_R_MIX_EN (1 << 3)
+
+/* ADC_L_CTRL bit fields */
+#define DA9055_ADC_L_EN (1 << 7)
+
+/* ADC_R_CTRL bit fields */
+#define DA9055_ADC_R_EN (1 << 7)
+
+/* DAC_L_CTRL bit fields */
+#define DA9055_DAC_L_MUTE_EN (1 << 6)
+
+/* DAC_R_CTRL bit fields */
+#define DA9055_DAC_R_MUTE_EN (1 << 6)
+
+/* HP_L_CTRL bit fields */
+#define DA9055_HP_L_AMP_OE (1 << 3)
+
+/* HP_R_CTRL bit fields */
+#define DA9055_HP_R_AMP_OE (1 << 3)
+
+/* LINE_CTRL bit fields */
+#define DA9055_LINE_AMP_OE (1 << 3)
+
+/* MIXOUT_L_CTRL bit fields */
+#define DA9055_MIXOUT_L_MIX_EN (1 << 3)
+
+/* MIXOUT_R_CTRL bit fields */
+#define DA9055_MIXOUT_R_MIX_EN (1 << 3)
+
+/* MIC bias select bit fields */
+#define DA9055_MICBIAS2_EN (1 << 6)
+
+/* ALC_CIC_OP_LEVEL_CTRL bit fields */
+#define DA9055_ALC_DATA_MIDDLE (2 << 0)
+#define DA9055_ALC_DATA_TOP (3 << 0)
+#define DA9055_ALC_CIC_OP_CHANNEL_LEFT (0 << 7)
+#define DA9055_ALC_CIC_OP_CHANNEL_RIGHT (1 << 7)
+
+#define DA9055_AIF_BCLK_MASK (3 << 0)
+#define DA9055_AIF_CLK_MODE_MASK (1 << 7)
+#define DA9055_AIF_FORMAT_MASK (3 << 0)
+#define DA9055_AIF_WORD_LENGTH_MASK (3 << 2)
+#define DA9055_GAIN_RAMPING_EN (1 << 5)
+#define DA9055_MICBIAS_LEVEL_MASK (3 << 4)
+
+#define DA9055_ALC_OFFSET_15_8 0x00FF00
+#define DA9055_ALC_OFFSET_17_16 0x030000
+#define DA9055_ALC_AVG_ITERATIONS 5
+
+struct pll_div {
+ int fref;
+ int fout;
+ u8 frac_top;
+ u8 frac_bot;
+ u8 integer;
+ u8 mode; /* 0 = slave, 1 = master */
+};
+
+/* PLL divisor table */
+static const struct pll_div da9055_pll_div[] = {
+ /* for MASTER mode, fs = 44.1Khz and its harmonics */
+ {11289600, 2822400, 0x00, 0x00, 0x20, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x03, 0x61, 0x1E, 1}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x0C, 0xCC, 0x1D, 1}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x19, 0x45, 0x1B, 1}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x18, 0x56, 0x1A, 1}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x02, 0xD0, 0x19, 1}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x1A, 0x1C, 0x12, 1}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x0B, 0x6D, 0x12, 1}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x07, 0xDD, 0x12, 1}, /* MCLK=19.8Mhz */
+ /* for MASTER mode, fs = 48Khz and its harmonics */
+ {11289600, 3072000, 0x1A, 0x8E, 0x22, 1}, /* MCLK=11.2896Mhz */
+ {12000000, 3072000, 0x18, 0x93, 0x20, 1}, /* MCLK=12Mhz */
+ {12288000, 3072000, 0x00, 0x00, 0x20, 1}, /* MCLK=12.288Mhz */
+ {13000000, 3072000, 0x07, 0xEA, 0x1E, 1}, /* MCLK=13Mhz */
+ {13500000, 3072000, 0x04, 0x11, 0x1D, 1}, /* MCLK=13.5Mhz */
+ {14400000, 3072000, 0x09, 0xD0, 0x1B, 1}, /* MCLK=14.4Mhz */
+ {19200000, 3072000, 0x0F, 0x5C, 0x14, 1}, /* MCLK=19.2Mhz */
+ {19680000, 3072000, 0x1F, 0x60, 0x13, 1}, /* MCLK=19.68Mhz */
+ {19800000, 3072000, 0x1B, 0x80, 0x13, 1}, /* MCLK=19.8Mhz */
+ /* for SLAVE mode with SRM */
+ {11289600, 2822400, 0x0D, 0x47, 0x21, 0}, /* MCLK=11.2896Mhz */
+ {12000000, 2822400, 0x0D, 0xFA, 0x1F, 0}, /* MCLK=12Mhz */
+ {12288000, 2822400, 0x16, 0x66, 0x1E, 0}, /* MCLK=12.288Mhz */
+ {13000000, 2822400, 0x00, 0x98, 0x1D, 0}, /* MCLK=13Mhz */
+ {13500000, 2822400, 0x1E, 0x33, 0x1B, 0}, /* MCLK=13.5Mhz */
+ {14400000, 2822400, 0x06, 0x50, 0x1A, 0}, /* MCLK=14.4Mhz */
+ {19200000, 2822400, 0x14, 0xBC, 0x13, 0}, /* MCLK=19.2Mhz */
+ {19680000, 2822400, 0x05, 0x66, 0x13, 0}, /* MCLK=19.68Mhz */
+ {19800000, 2822400, 0x01, 0xAE, 0x13, 0}, /* MCLK=19.8Mhz */
+};
+
+enum clk_src {
+ DA9055_CLKSRC_MCLK
+};
+
+/* Gain and Volume */
+
+static const unsigned int aux_vol_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0),
+ /* -54dB to 15dB */
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+};
+
+static const unsigned int digital_gain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* -78dB to 12dB */
+ 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0)
+};
+
+static const unsigned int alc_analog_gain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+ /* 0dB to 36dB */
+ 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0)
+};
+
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0);
+
+/* ADC and DAC high pass filter cutoff value */
+static const char * const da9055_hpf_cutoff_txt[] = {
+ "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000"
+};
+
+static const struct soc_enum da9055_dac_hpf_cutoff =
+ SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt);
+
+static const struct soc_enum da9055_adc_hpf_cutoff =
+ SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt);
+
+/* ADC and DAC voice mode (8kHz) high pass cutoff value */
+static const char * const da9055_vf_cutoff_txt[] = {
+ "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da9055_dac_vf_cutoff =
+ SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 0, 8, da9055_vf_cutoff_txt);
+
+static const struct soc_enum da9055_adc_vf_cutoff =
+ SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 0, 8, da9055_vf_cutoff_txt);
+
+/* Gain ramping rate value */
+static const char * const da9055_gain_ramping_txt[] = {
+ "nominal rate", "nominal rate * 4", "nominal rate * 8",
+ "nominal rate / 8"
+};
+
+static const struct soc_enum da9055_gain_ramping_rate =
+ SOC_ENUM_SINGLE(DA9055_GAIN_RAMP_CTRL, 0, 4, da9055_gain_ramping_txt);
+
+/* DAC noise gate setup time value */
+static const char * const da9055_dac_ng_setup_time_txt[] = {
+ "256 samples", "512 samples", "1024 samples", "2048 samples"
+};
+
+static const struct soc_enum da9055_dac_ng_setup_time =
+ SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 0, 4,
+ da9055_dac_ng_setup_time_txt);
+
+/* DAC noise gate rampup rate value */
+static const char * const da9055_dac_ng_rampup_txt[] = {
+ "0.02 ms/dB", "0.16 ms/dB"
+};
+
+static const struct soc_enum da9055_dac_ng_rampup_rate =
+ SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 2, 2,
+ da9055_dac_ng_rampup_txt);
+
+/* DAC noise gate rampdown rate value */
+static const char * const da9055_dac_ng_rampdown_txt[] = {
+ "0.64 ms/dB", "20.48 ms/dB"
+};
+
+static const struct soc_enum da9055_dac_ng_rampdown_rate =
+ SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 3, 2,
+ da9055_dac_ng_rampdown_txt);
+
+/* DAC soft mute rate value */
+static const char * const da9055_dac_soft_mute_rate_txt[] = {
+ "1", "2", "4", "8", "16", "32", "64"
+};
+
+static const struct soc_enum da9055_dac_soft_mute_rate =
+ SOC_ENUM_SINGLE(DA9055_DAC_FILTERS5, 4, 7,
+ da9055_dac_soft_mute_rate_txt);
+
+/* DAC routing select */
+static const char * const da9055_dac_src_txt[] = {
+ "ADC output left", "ADC output right", "AIF input left",
+ "AIF input right"
+};
+
+static const struct soc_enum da9055_dac_l_src =
+ SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 0, 4, da9055_dac_src_txt);
+
+static const struct soc_enum da9055_dac_r_src =
+ SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 4, 4, da9055_dac_src_txt);
+
+/* MIC PGA Left source select */
+static const char * const da9055_mic_l_src_txt[] = {
+ "MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L"
+};
+
+static const struct soc_enum da9055_mic_l_src =
+ SOC_ENUM_SINGLE(DA9055_MIXIN_L_SELECT, 4, 4, da9055_mic_l_src_txt);
+
+/* MIC PGA Right source select */
+static const char * const da9055_mic_r_src_txt[] = {
+ "MIC2_R_L", "MIC2_R", "MIC2_L"
+};
+
+static const struct soc_enum da9055_mic_r_src =
+ SOC_ENUM_SINGLE(DA9055_MIXIN_R_SELECT, 4, 3, da9055_mic_r_src_txt);
+
+/* ALC Input Signal Tracking rate select */
+static const char * const da9055_signal_tracking_rate_txt[] = {
+ "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da9055_integ_attack_rate =
+ SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 4, 4,
+ da9055_signal_tracking_rate_txt);
+
+static const struct soc_enum da9055_integ_release_rate =
+ SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 6, 4,
+ da9055_signal_tracking_rate_txt);
+
+/* ALC Attack Rate select */
+static const char * const da9055_attack_rate_txt[] = {
+ "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs",
+ "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static const struct soc_enum da9055_attack_rate =
+ SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 0, 13, da9055_attack_rate_txt);
+
+/* ALC Release Rate select */
+static const char * const da9055_release_rate_txt[] = {
+ "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs",
+ "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs"
+};
+
+static const struct soc_enum da9055_release_rate =
+ SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 4, 11, da9055_release_rate_txt);
+
+/* ALC Hold Time select */
+static const char * const da9055_hold_time_txt[] = {
+ "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+ "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+ "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da9055_hold_time =
+ SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 0, 16, da9055_hold_time_txt);
+
+static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+{
+ int mid_data, top_data;
+ int sum = 0;
+ u8 iteration;
+
+ for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
+ iteration++) {
+ /* Select the left or right channel and capture data */
+ snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
+
+ /* Select middle 8 bits for read back from data register */
+ snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA9055_ALC_DATA_MIDDLE);
+ mid_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+
+ /* Select top 8 bits for read back from data register */
+ snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ reg_val | DA9055_ALC_DATA_TOP);
+ top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+
+ sum += ((mid_data << 8) | (top_data << 16));
+ }
+
+ return sum / DA9055_ALC_AVG_ITERATIONS;
+}
+
+static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 reg_val, adc_left, adc_right;
+ int avg_left_data, avg_right_data, offset_l, offset_r;
+
+ if (ucontrol->value.integer.value[0]) {
+ /*
+ * While enabling ALC (or ALC sync mode), calibration of the DC
+ * offsets must be done first
+ */
+
+ /* Save current values from ADC control registers */
+ adc_left = snd_soc_read(codec, DA9055_ADC_L_CTRL);
+ adc_right = snd_soc_read(codec, DA9055_ADC_R_CTRL);
+
+ /* Enable ADC Left and Right */
+ snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ DA9055_ADC_L_EN, DA9055_ADC_L_EN);
+ snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ DA9055_ADC_R_EN, DA9055_ADC_R_EN);
+
+ /* Calculate average for Left and Right data */
+ /* Left Data */
+ avg_left_data = da9055_get_alc_data(codec,
+ DA9055_ALC_CIC_OP_CHANNEL_LEFT);
+ /* Right Data */
+ avg_right_data = da9055_get_alc_data(codec,
+ DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
+
+ /* Calculate DC offset */
+ offset_l = -avg_left_data;
+ offset_r = -avg_right_data;
+
+ reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_L, reg_val);
+ reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16;
+ snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_L, reg_val);
+
+ reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
+ snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_R, reg_val);
+ reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16;
+ snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_R, reg_val);
+
+ /* Restore original values of ADC control registers */
+ snd_soc_write(codec, DA9055_ADC_L_CTRL, adc_left);
+ snd_soc_write(codec, DA9055_ADC_R_CTRL, adc_right);
+ }
+
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new da9055_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ DA9055_MIC_L_GAIN, DA9055_MIC_R_GAIN,
+ 0, 0x7, 0, mic_vol_tlv),
+ SOC_DOUBLE_R_TLV("Aux Volume",
+ DA9055_AUX_L_GAIN, DA9055_AUX_R_GAIN,
+ 0, 0x3f, 0, aux_vol_tlv),
+ SOC_DOUBLE_R_TLV("Mixin PGA Volume",
+ DA9055_MIXIN_L_GAIN, DA9055_MIXIN_R_GAIN,
+ 0, 0xf, 0, mixin_gain_tlv),
+ SOC_DOUBLE_R_TLV("ADC Volume",
+ DA9055_ADC_L_GAIN, DA9055_ADC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC Volume",
+ DA9055_DAC_L_GAIN, DA9055_DAC_R_GAIN,
+ 0, 0x7f, 0, digital_gain_tlv),
+ SOC_DOUBLE_R_TLV("Headphone Volume",
+ DA9055_HP_L_GAIN, DA9055_HP_R_GAIN,
+ 0, 0x3f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Lineout Volume", DA9055_LINE_GAIN, 0, 0x3f, 0,
+ lineout_vol_tlv),
+
+ /* DAC Equalizer controls */
+ SOC_SINGLE("DAC EQ Switch", DA9055_DAC_FILTERS4, 7, 1, 0),
+ SOC_SINGLE_TLV("DAC EQ1 Volume", DA9055_DAC_FILTERS2, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ2 Volume", DA9055_DAC_FILTERS2, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ3 Volume", DA9055_DAC_FILTERS3, 0, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ4 Volume", DA9055_DAC_FILTERS3, 4, 0xf, 0,
+ eq_gain_tlv),
+ SOC_SINGLE_TLV("DAC EQ5 Volume", DA9055_DAC_FILTERS4, 0, 0xf, 0,
+ eq_gain_tlv),
+
+ /* High Pass Filter and Voice Mode controls */
+ SOC_SINGLE("ADC HPF Switch", DA9055_ADC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("ADC HPF Cutoff", da9055_adc_hpf_cutoff),
+ SOC_SINGLE("ADC Voice Mode Switch", DA9055_ADC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("ADC Voice Cutoff", da9055_adc_vf_cutoff),
+
+ SOC_SINGLE("DAC HPF Switch", DA9055_DAC_FILTERS1, 7, 1, 0),
+ SOC_ENUM("DAC HPF Cutoff", da9055_dac_hpf_cutoff),
+ SOC_SINGLE("DAC Voice Mode Switch", DA9055_DAC_FILTERS1, 3, 1, 0),
+ SOC_ENUM("DAC Voice Cutoff", da9055_dac_vf_cutoff),
+
+ /* Mute controls */
+ SOC_DOUBLE_R("Mic Switch", DA9055_MIC_L_CTRL,
+ DA9055_MIC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Aux Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("ADC Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 6, 1, 0),
+ SOC_DOUBLE_R("Headphone Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 6, 1, 0),
+ SOC_SINGLE("Lineout Switch", DA9055_LINE_CTRL, 6, 1, 0),
+ SOC_SINGLE("DAC Soft Mute Switch", DA9055_DAC_FILTERS5, 7, 1, 0),
+ SOC_ENUM("DAC Soft Mute Rate", da9055_dac_soft_mute_rate),
+
+ /* Zero Cross controls */
+ SOC_DOUBLE_R("Aux ZC Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Mixin PGA ZC Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 4, 1, 0),
+ SOC_DOUBLE_R("Headphone ZC Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 4, 1, 0),
+ SOC_SINGLE("Lineout ZC Switch", DA9055_LINE_CTRL, 4, 1, 0),
+
+ /* Gain Ramping controls */
+ SOC_DOUBLE_R("Aux Gain Ramping Switch", DA9055_AUX_L_CTRL,
+ DA9055_AUX_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("ADC Gain Ramping Switch", DA9055_ADC_L_CTRL,
+ DA9055_ADC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("DAC Gain Ramping Switch", DA9055_DAC_L_CTRL,
+ DA9055_DAC_R_CTRL, 5, 1, 0),
+ SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA9055_HP_L_CTRL,
+ DA9055_HP_R_CTRL, 5, 1, 0),
+ SOC_SINGLE("Lineout Gain Ramping Switch", DA9055_LINE_CTRL, 5, 1, 0),
+ SOC_ENUM("Gain Ramping Rate", da9055_gain_ramping_rate),
+
+ /* DAC Noise Gate controls */
+ SOC_SINGLE("DAC NG Switch", DA9055_DAC_NG_CTRL, 7, 1, 0),
+ SOC_SINGLE("DAC NG ON Threshold", DA9055_DAC_NG_ON_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_SINGLE("DAC NG OFF Threshold", DA9055_DAC_NG_OFF_THRESHOLD,
+ 0, 0x7, 0),
+ SOC_ENUM("DAC NG Setup Time", da9055_dac_ng_setup_time),
+ SOC_ENUM("DAC NG Rampup Rate", da9055_dac_ng_rampup_rate),
+ SOC_ENUM("DAC NG Rampdown Rate", da9055_dac_ng_rampdown_rate),
+
+ /* DAC Invertion control */
+ SOC_SINGLE("DAC Left Invert", DA9055_DIG_CTRL, 3, 1, 0),
+ SOC_SINGLE("DAC Right Invert", DA9055_DIG_CTRL, 7, 1, 0),
+
+ /* DMIC controls */
+ SOC_DOUBLE_R("DMIC Switch", DA9055_MIXIN_L_SELECT,
+ DA9055_MIXIN_R_SELECT, 7, 1, 0),
+
+ /* ALC Controls */
+ SOC_DOUBLE_EXT("ALC Switch", DA9055_ALC_CTRL1, 3, 7, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE_EXT("ALC Sync Mode Switch", DA9055_ALC_CTRL1, 1, 1, 0,
+ snd_soc_get_volsw, da9055_put_alc_sw),
+ SOC_SINGLE("ALC Offset Switch", DA9055_ALC_CTRL1, 0, 1, 0),
+ SOC_SINGLE("ALC Anticlip Mode Switch", DA9055_ALC_ANTICLIP_CTRL,
+ 7, 1, 0),
+ SOC_SINGLE("ALC Anticlip Level", DA9055_ALC_ANTICLIP_LEVEL,
+ 0, 0x7f, 0),
+ SOC_SINGLE_TLV("ALC Min Threshold Volume", DA9055_ALC_TARGET_MIN,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Threshold Volume", DA9055_ALC_TARGET_MAX,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA9055_ALC_NOISE,
+ 0, 0x3f, 1, alc_threshold_tlv),
+ SOC_SINGLE_TLV("ALC Max Gain Volume", DA9055_ALC_GAIN_LIMITS,
+ 4, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA9055_ALC_GAIN_LIMITS,
+ 0, 0xf, 0, alc_gain_tlv),
+ SOC_SINGLE_TLV("ALC Min Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 0, 0x7, 0, alc_analog_gain_tlv),
+ SOC_SINGLE_TLV("ALC Max Analog Gain Volume",
+ DA9055_ALC_ANA_GAIN_LIMITS,
+ 4, 0x7, 0, alc_analog_gain_tlv),
+ SOC_ENUM("ALC Attack Rate", da9055_attack_rate),
+ SOC_ENUM("ALC Release Rate", da9055_release_rate),
+ SOC_ENUM("ALC Hold Time", da9055_hold_time),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * larger
+ */
+ SOC_ENUM("ALC Integ Attack Rate", da9055_integ_attack_rate),
+ /*
+ * Rate at which input signal envelope is tracked as the signal gets
+ * smaller
+ */
+ SOC_ENUM("ALC Integ Release Rate", da9055_integ_release_rate),
+};
+
+/* DAPM Controls */
+
+/* Mic PGA Left Source */
+static const struct snd_kcontrol_new da9055_mic_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_l_src);
+
+/* Mic PGA Right Source */
+static const struct snd_kcontrol_new da9055_mic_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_mic_r_src);
+
+/* In Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixinl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXIN_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_L_SELECT, 2, 1, 0),
+};
+
+/* In Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixinr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXIN_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXIN_R_SELECT, 3, 1, 0),
+};
+
+/* DAC Left Source */
+static const struct snd_kcontrol_new da9055_dac_l_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_l_src);
+
+/* DAC Right Source */
+static const struct snd_kcontrol_new da9055_dac_r_mux_controls =
+SOC_DAPM_ENUM("Route", da9055_dac_r_src);
+
+/* Out Mixer Left */
+static const struct snd_kcontrol_new da9055_dapm_mixoutl_controls[] = {
+ SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXOUT_L_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_L_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_L_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", DA9055_MIXOUT_L_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_L_SELECT,
+ 6, 1, 0),
+};
+
+/* Out Mixer Right */
+static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = {
+ SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXOUT_R_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_R_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_R_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", DA9055_MIXOUT_R_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Aux Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 4, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 5, 1, 0),
+ SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_R_SELECT,
+ 6, 1, 0),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
+ /* Input Side */
+
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Mic Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_l_mux_controls),
+ SND_SOC_DAPM_MUX("Mic Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_mic_r_mux_controls),
+
+ /* Input PGAs */
+ SND_SOC_DAPM_PGA("Mic Left", DA9055_MIC_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Right", DA9055_MIC_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Left", DA9055_AUX_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Aux Right", DA9055_AUX_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Left", DA9055_MIXIN_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXIN Right", DA9055_MIXIN_R_CTRL, 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Mic Bias", DA9055_MIC_BIAS_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("AIF", DA9055_AIF_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Charge Pump", DA9055_CP_CTRL, 7, 0, NULL, 0),
+
+ /* Input Mixers */
+ SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinl_controls)),
+ SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixinr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixinr_controls)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC Left", "Capture", DA9055_ADC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Capture", DA9055_ADC_R_CTRL, 7, 0),
+
+ /* Output Side */
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC Left Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_l_mux_controls),
+ SND_SOC_DAPM_MUX("DAC Right Source", SND_SOC_NOPM, 0, 0,
+ &da9055_dac_r_mux_controls),
+
+ /* AIF input */
+ SND_SOC_DAPM_AIF_IN("AIFIN Left", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFIN Right", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Left", "Playback", DA9055_DAC_L_CTRL, 7, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Playback", DA9055_DAC_R_CTRL, 7, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutl_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutl_controls)),
+ SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_mixoutr_controls[0],
+ ARRAY_SIZE(da9055_dapm_mixoutr_controls)),
+
+ /* Output PGAs */
+ SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Lineout", DA9055_LINE_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Left", DA9055_HP_L_CTRL, 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Headphone Right", DA9055_HP_R_CTRL, 7, 0, NULL, 0),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LINE"),
+};
+
+/* DAPM audio route definition */
+static const struct snd_soc_dapm_route da9055_audio_map[] = {
+ /* Dest Connecting Widget source */
+
+ /* Input path */
+ {"Mic Left Source", "MIC1_P_N", "MIC1"},
+ {"Mic Left Source", "MIC1_P", "MIC1"},
+ {"Mic Left Source", "MIC1_N", "MIC1"},
+ {"Mic Left Source", "MIC2_L", "MIC2"},
+
+ {"Mic Right Source", "MIC2_R_L", "MIC2"},
+ {"Mic Right Source", "MIC2_R", "MIC2"},
+ {"Mic Right Source", "MIC2_L", "MIC2"},
+
+ {"Mic Left", NULL, "Mic Left Source"},
+ {"Mic Right", NULL, "Mic Right Source"},
+
+ {"Aux Left", NULL, "AUXL"},
+ {"Aux Right", NULL, "AUXR"},
+
+ {"In Mixer Left", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Left", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Left", "Aux Left Switch", "Aux Left"},
+
+ {"In Mixer Right", "Mic Right Switch", "Mic Right"},
+ {"In Mixer Right", "Mic Left Switch", "Mic Left"},
+ {"In Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"In Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+
+ {"MIXIN Left", NULL, "In Mixer Left"},
+ {"ADC Left", NULL, "MIXIN Left"},
+
+ {"MIXIN Right", NULL, "In Mixer Right"},
+ {"ADC Right", NULL, "MIXIN Right"},
+
+ {"ADC Left", NULL, "AIF"},
+ {"ADC Right", NULL, "AIF"},
+
+ /* Output path */
+ {"AIFIN Left", NULL, "AIF"},
+ {"AIFIN Right", NULL, "AIF"},
+
+ {"DAC Left Source", "ADC output left", "ADC Left"},
+ {"DAC Left Source", "ADC output right", "ADC Right"},
+ {"DAC Left Source", "AIF input left", "AIFIN Left"},
+ {"DAC Left Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Right Source", "ADC output left", "ADC Left"},
+ {"DAC Right Source", "ADC output right", "ADC Right"},
+ {"DAC Right Source", "AIF input left", "AIFIN Left"},
+ {"DAC Right Source", "AIF input right", "AIFIN Right"},
+
+ {"DAC Left", NULL, "DAC Left Source"},
+ {"DAC Right", NULL, "DAC Right Source"},
+
+ {"Out Mixer Left", "Aux Left Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Left", "Aux Left Invert Switch", "Aux Left"},
+ {"Out Mixer Left", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Left", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Left", "DAC Left Switch", "DAC Left"},
+
+ {"Out Mixer Right", "Aux Right Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Switch", "MIXIN Left"},
+ {"Out Mixer Right", "Aux Right Invert Switch", "Aux Right"},
+ {"Out Mixer Right", "Mixin Right Invert Switch", "MIXIN Right"},
+ {"Out Mixer Right", "Mixin Left Invert Switch", "MIXIN Left"},
+ {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
+
+ {"MIXOUT Left", NULL, "Out Mixer Left"},
+ {"Headphone Left", NULL, "MIXOUT Left"},
+ {"Headphone Left", NULL, "Charge Pump"},
+ {"HPL", NULL, "Headphone Left"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Headphone Right", NULL, "MIXOUT Right"},
+ {"Headphone Right", NULL, "Charge Pump"},
+ {"HPR", NULL, "Headphone Right"},
+
+ {"MIXOUT Right", NULL, "Out Mixer Right"},
+ {"Lineout", NULL, "MIXOUT Right"},
+ {"LINE", NULL, "Lineout"},
+};
+
+/* Codec private data */
+struct da9055_priv {
+ struct regmap *regmap;
+ unsigned int mclk_rate;
+ int master;
+ struct da9055_platform_data *pdata;
+};
+
+static struct reg_default da9055_reg_defaults[] = {
+ { 0x21, 0x10 },
+ { 0x22, 0x0A },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x0C },
+ { 0x28, 0x01 },
+ { 0x29, 0x08 },
+ { 0x2A, 0x32 },
+ { 0x2B, 0x00 },
+ { 0x30, 0x35 },
+ { 0x31, 0x35 },
+ { 0x32, 0x00 },
+ { 0x33, 0x00 },
+ { 0x34, 0x03 },
+ { 0x35, 0x03 },
+ { 0x36, 0x6F },
+ { 0x37, 0x6F },
+ { 0x38, 0x80 },
+ { 0x39, 0x01 },
+ { 0x3A, 0x01 },
+ { 0x40, 0x00 },
+ { 0x41, 0x88 },
+ { 0x42, 0x88 },
+ { 0x43, 0x08 },
+ { 0x44, 0x80 },
+ { 0x45, 0x6F },
+ { 0x46, 0x6F },
+ { 0x47, 0x61 },
+ { 0x48, 0x35 },
+ { 0x49, 0x35 },
+ { 0x4A, 0x35 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x60, 0x44 },
+ { 0x61, 0x44 },
+ { 0x62, 0x00 },
+ { 0x63, 0x40 },
+ { 0x64, 0x40 },
+ { 0x65, 0x40 },
+ { 0x66, 0x40 },
+ { 0x67, 0x40 },
+ { 0x68, 0x40 },
+ { 0x69, 0x48 },
+ { 0x6A, 0x40 },
+ { 0x6B, 0x41 },
+ { 0x6C, 0x40 },
+ { 0x6D, 0x40 },
+ { 0x6E, 0x10 },
+ { 0x6F, 0x10 },
+ { 0x90, 0x80 },
+ { 0x92, 0x02 },
+ { 0x93, 0x00 },
+ { 0x99, 0x00 },
+ { 0x9A, 0x00 },
+ { 0x9B, 0x00 },
+ { 0x9C, 0x3F },
+ { 0x9D, 0x00 },
+ { 0x9E, 0x3F },
+ { 0x9F, 0xFF },
+ { 0xA0, 0x71 },
+ { 0xA1, 0x00 },
+ { 0xA2, 0x00 },
+ { 0xA6, 0x00 },
+ { 0xA7, 0x00 },
+ { 0xAB, 0x00 },
+ { 0xAC, 0x00 },
+ { 0xAD, 0x00 },
+ { 0xAF, 0x08 },
+ { 0xB0, 0x00 },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x00 },
+};
+
+static bool da9055_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case DA9055_STATUS1:
+ case DA9055_PLL_STATUS:
+ case DA9055_AUX_L_GAIN_STATUS:
+ case DA9055_AUX_R_GAIN_STATUS:
+ case DA9055_MIC_L_GAIN_STATUS:
+ case DA9055_MIC_R_GAIN_STATUS:
+ case DA9055_MIXIN_L_GAIN_STATUS:
+ case DA9055_MIXIN_R_GAIN_STATUS:
+ case DA9055_ADC_L_GAIN_STATUS:
+ case DA9055_ADC_R_GAIN_STATUS:
+ case DA9055_DAC_L_GAIN_STATUS:
+ case DA9055_DAC_R_GAIN_STATUS:
+ case DA9055_HP_L_GAIN_STATUS:
+ case DA9055_HP_R_GAIN_STATUS:
+ case DA9055_LINE_GAIN_STATUS:
+ case DA9055_ALC_CIC_OP_LVL_DATA:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Set DAI word length */
+static int da9055_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ u8 aif_ctrl, fs;
+ u32 sysclk;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ aif_ctrl = DA9055_AIF_WORD_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ aif_ctrl = DA9055_AIF_WORD_S20_3LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ aif_ctrl = DA9055_AIF_WORD_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ aif_ctrl = DA9055_AIF_WORD_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set AIF format */
+ snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ aif_ctrl);
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs = DA9055_SR_8000;
+ sysclk = 3072000;
+ break;
+ case 11025:
+ fs = DA9055_SR_11025;
+ sysclk = 2822400;
+ break;
+ case 12000:
+ fs = DA9055_SR_12000;
+ sysclk = 3072000;
+ break;
+ case 16000:
+ fs = DA9055_SR_16000;
+ sysclk = 3072000;
+ break;
+ case 22050:
+ fs = DA9055_SR_22050;
+ sysclk = 2822400;
+ break;
+ case 32000:
+ fs = DA9055_SR_32000;
+ sysclk = 3072000;
+ break;
+ case 44100:
+ fs = DA9055_SR_44100;
+ sysclk = 2822400;
+ break;
+ case 48000:
+ fs = DA9055_SR_48000;
+ sysclk = 3072000;
+ break;
+ case 88200:
+ fs = DA9055_SR_88200;
+ sysclk = 2822400;
+ break;
+ case 96000:
+ fs = DA9055_SR_96000;
+ sysclk = 3072000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (da9055->mclk_rate) {
+ /* PLL Mode, Write actual FS */
+ snd_soc_write(codec, DA9055_SR, fs);
+ } else {
+ /*
+ * Non-PLL Mode
+ * When PLL is bypassed, chip assumes constant MCLK of
+ * 12.288MHz and uses sample rate value to divide this MCLK
+ * to derive its sys clk. As sys clk has to be 256 * Fs, we
+ * need to write constant sample rate i.e. 48KHz.
+ */
+ snd_soc_write(codec, DA9055_SR, DA9055_SR_48000);
+ }
+
+ if (da9055->mclk_rate && (da9055->mclk_rate != sysclk)) {
+ /* PLL Mode */
+ if (!da9055->master) {
+ /* PLL slave mode, enable PLL and also SRM */
+ snd_soc_update_bits(codec, DA9055_PLL_CTRL,
+ DA9055_PLL_EN | DA9055_PLL_SRM_EN,
+ DA9055_PLL_EN | DA9055_PLL_SRM_EN);
+ } else {
+ /* PLL master mode, only enable PLL */
+ snd_soc_update_bits(codec, DA9055_PLL_CTRL,
+ DA9055_PLL_EN, DA9055_PLL_EN);
+ }
+ } else {
+ /* Non PLL Mode, disable PLL */
+ snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ }
+
+ return 0;
+}
+
+/* Set DAI mode and Format */
+static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ u8 aif_clk_mode, aif_ctrl, mode;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* DA9055 in I2S Master Mode */
+ mode = 1;
+ aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* DA9055 in I2S Slave Mode */
+ mode = 0;
+ aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Don't allow change of mode if PLL is enabled */
+ if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ (da9055->master != mode))
+ return -EINVAL;
+
+ da9055->master = mode;
+
+ /* Only I2S is supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aif_ctrl = DA9055_AIF_FORMAT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 32 BCLK per WCLK is supported */
+ aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
+
+ snd_soc_update_bits(codec, DA9055_AIF_CLK_MODE,
+ (DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK),
+ aif_clk_mode);
+ snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
+ aif_ctrl);
+ return 0;
+}
+
+static int da9055_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
+ } else {
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_DAC_L_MUTE_EN, 0);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_DAC_R_MUTE_EN, 0);
+ }
+
+ return 0;
+}
+
+#define DA9055_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case DA9055_CLKSRC_MCLK:
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 13500000:
+ case 14400000:
+ case 19200000:
+ case 19680000:
+ case 19800000:
+ da9055->mclk_rate = freq;
+ return 0;
+ default:
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+}
+
+/*
+ * da9055_set_dai_pll : Configure the codec PLL
+ * @param codec_dai : Pointer to codec DAI
+ * @param pll_id : da9055 has only one pll, so pll_id is always zero
+ * @param fref : Input MCLK frequency
+ * @param fout : FsDM value
+ * @return int : Zero for success, negative error code for error
+ *
+ * Note: Supported PLL input frequencies are 11.2896MHz, 12MHz, 12.288MHz,
+ * 13MHz, 13.5MHz, 14.4MHz, 19.2MHz, 19.6MHz and 19.8MHz
+ */
+static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ u8 pll_frac_top, pll_frac_bot, pll_integer, cnt;
+
+ /* Disable PLL before setting the divisors */
+ snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+
+ /* In slave mode, there is only one set of divisors */
+ if (!da9055->master && (fout != 2822400))
+ goto pll_err;
+
+ /* Search pll div array for correct divisors */
+ for (cnt = 0; cnt < ARRAY_SIZE(da9055_pll_div); cnt++) {
+ /* Check fref, mode and fout */
+ if ((fref == da9055_pll_div[cnt].fref) &&
+ (da9055->master == da9055_pll_div[cnt].mode) &&
+ (fout == da9055_pll_div[cnt].fout)) {
+ /* All match, pick up divisors */
+ pll_frac_top = da9055_pll_div[cnt].frac_top;
+ pll_frac_bot = da9055_pll_div[cnt].frac_bot;
+ pll_integer = da9055_pll_div[cnt].integer;
+ break;
+ }
+ }
+ if (cnt >= ARRAY_SIZE(da9055_pll_div))
+ goto pll_err;
+
+ /* Write PLL dividers */
+ snd_soc_write(codec, DA9055_PLL_FRAC_TOP, pll_frac_top);
+ snd_soc_write(codec, DA9055_PLL_FRAC_BOT, pll_frac_bot);
+ snd_soc_write(codec, DA9055_PLL_INTEGER, pll_integer);
+
+ return 0;
+pll_err:
+ dev_err(codec_dai->dev, "Error in setting up PLL\n");
+ return -EINVAL;
+}
+
+/* DAI operations */
+static const struct snd_soc_dai_ops da9055_dai_ops = {
+ .hw_params = da9055_hw_params,
+ .set_fmt = da9055_set_dai_fmt,
+ .set_sysclk = da9055_set_dai_sysclk,
+ .set_pll = da9055_set_dai_pll,
+ .digital_mute = da9055_mute,
+};
+
+static struct snd_soc_dai_driver da9055_dai = {
+ .name = "da9055-hifi",
+ /* Playback Capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ /* Capture Capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = DA9055_FORMATS,
+ },
+ .ops = &da9055_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int da9055_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Enable VMID reference & master bias */
+ snd_soc_update_bits(codec, DA9055_REFERENCES,
+ DA9055_VMID_EN | DA9055_BIAS_EN,
+ DA9055_VMID_EN | DA9055_BIAS_EN);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Disable VMID reference & master bias */
+ snd_soc_update_bits(codec, DA9055_REFERENCES,
+ DA9055_VMID_EN | DA9055_BIAS_EN, 0);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int da9055_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+
+ codec->control_data = da9055->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable all Gain Ramps */
+ snd_soc_update_bits(codec, DA9055_AUX_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_AUX_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+ snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
+
+ /*
+ * There are two separate control bits for input and output mixers as
+ * well as headphone and line outs.
+ * One to enable corresponding amplifier and other to enable its
+ * output. As amplifier bits are related to power control, they are
+ * being managed by DAPM while other (non power related) bits are
+ * enabled here
+ */
+ snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
+
+ snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL,
+ DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
+ snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
+ DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN);
+
+ snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ DA9055_HP_L_AMP_OE, DA9055_HP_L_AMP_OE);
+ snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ DA9055_HP_R_AMP_OE, DA9055_HP_R_AMP_OE);
+
+ snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ DA9055_LINE_AMP_OE, DA9055_LINE_AMP_OE);
+
+ /* Set this as per your system configuration */
+ snd_soc_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
+
+ /* Set platform data values */
+ if (da9055->pdata) {
+ /* set mic bias source */
+ if (da9055->pdata->micbias_source) {
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ DA9055_MICBIAS2_EN,
+ DA9055_MICBIAS2_EN);
+ } else {
+ snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ DA9055_MICBIAS2_EN, 0);
+ }
+ /* set mic bias voltage */
+ switch (da9055->pdata->micbias) {
+ case DA9055_MICBIAS_2_2V:
+ case DA9055_MICBIAS_2_1V:
+ case DA9055_MICBIAS_1_8V:
+ case DA9055_MICBIAS_1_6V:
+ snd_soc_update_bits(codec, DA9055_MIC_CONFIG,
+ DA9055_MICBIAS_LEVEL_MASK,
+ (da9055->pdata->micbias) << 4);
+ break;
+ }
+ }
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_da9055 = {
+ .probe = da9055_probe,
+ .set_bias_level = da9055_set_bias_level,
+
+ .controls = da9055_snd_controls,
+ .num_controls = ARRAY_SIZE(da9055_snd_controls),
+
+ .dapm_widgets = da9055_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets),
+ .dapm_routes = da9055_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da9055_audio_map),
+};
+
+static const struct regmap_config da9055_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .reg_defaults = da9055_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(da9055_reg_defaults),
+ .volatile_reg = da9055_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit da9055_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9055_priv *da9055;
+ struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ int ret;
+
+ da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055_priv),
+ GFP_KERNEL);
+ if (!da9055)
+ return -ENOMEM;
+
+ if (pdata)
+ da9055->pdata = pdata;
+
+ i2c_set_clientdata(i2c, da9055);
+
+ da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config);
+ if (IS_ERR(da9055->regmap)) {
+ ret = PTR_ERR(da9055->regmap);
+ dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_da9055, &da9055_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register da9055 codec: %d\n",
+ ret);
+ }
+ return ret;
+}
+
+static int __devexit da9055_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id da9055_i2c_id[] = {
+ { "da9055", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
+
+/* I2C codec control layer */
+static struct i2c_driver da9055_i2c_driver = {
+ .driver = {
+ .name = "da9055",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9055_i2c_probe,
+ .remove = __devexit_p(da9055_remove),
+ .id_table = da9055_i2c_id,
+};
+
+module_i2c_driver(da9055_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA9055 Codec driver");
+MODULE_AUTHOR("David Chen, Ashish Chavan");
+MODULE_LICENSE("GPL");
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
.id_table = lm4857_i2c_id,
};
-static int __init lm4857_init(void)
-{
- return i2c_add_driver(&lm4857_i2c_driver);
-}
-module_init(lm4857_init);
-
-static void __exit lm4857_exit(void)
-{
- i2c_del_driver(&lm4857_i2c_driver);
-}
-module_exit(lm4857_exit);
+module_i2c_driver(lm4857_i2c_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("LM4857 amplifier driver");
.id_table = max98088_i2c_id,
};
-static int __init max98088_init(void)
-{
- int ret;
-
- ret = i2c_add_driver(&max98088_i2c_driver);
- if (ret)
- pr_err("Failed to register max98088 I2C driver: %d\n", ret);
-
- return ret;
-}
-module_init(max98088_init);
-
-static void __exit max98088_exit(void)
-{
- i2c_del_driver(&max98088_i2c_driver);
-}
-module_exit(max98088_exit);
+module_i2c_driver(max98088_i2c_driver);
MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
.id_table = max98095_i2c_id,
};
-static int __init max98095_init(void)
-{
- int ret;
-
- ret = i2c_add_driver(&max98095_i2c_driver);
- if (ret)
- pr_err("Failed to register max98095 I2C driver: %d\n", ret);
-
- return ret;
-}
-module_init(max98095_init);
-
-static void __exit max98095_exit(void)
-{
- i2c_del_driver(&max98095_i2c_driver);
-}
-module_exit(max98095_exit);
+module_i2c_driver(max98095_i2c_driver);
MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
MODULE_AUTHOR("Peter Hsiang");
.id_table = max9850_i2c_id,
};
-static int __init max9850_init(void)
-{
- return i2c_add_driver(&max9850_i2c_driver);
-}
-module_init(max9850_init);
-
-static void __exit max9850_exit(void)
-{
- i2c_del_driver(&max9850_i2c_driver);
-}
-module_exit(max9850_exit);
+module_i2c_driver(max9850_i2c_driver);
MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
.id_table = max9877_i2c_id,
};
-static int __init max9877_init(void)
-{
- return i2c_add_driver(&max9877_i2c_driver);
-}
-module_init(max9877_init);
-
-static void __exit max9877_exit(void)
-{
- i2c_del_driver(&max9877_i2c_driver);
-}
-module_exit(max9877_exit);
+module_i2c_driver(max9877_i2c_driver);
MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
}
static const struct snd_kcontrol_new mc1l_amp_ctl =
- SOC_DAPM_SINGLE("Switch", 38, 7, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 7, 1, 0);
static const struct snd_kcontrol_new mc1r_amp_ctl =
- SOC_DAPM_SINGLE("Switch", 38, 5, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 5, 1, 0);
static const struct snd_kcontrol_new mc2_amp_ctl =
- SOC_DAPM_SINGLE("Switch", 38, 9, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 9, 1, 0);
static const struct snd_kcontrol_new atx_amp_ctl =
- SOC_DAPM_SINGLE("Switch", 38, 11, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 11, 1, 0);
/* Virtual mux. The chip does the input selection automatically
SOC_DAPM_ENUM_VIRT("Route", adcr_enum);
static const struct snd_kcontrol_new samp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 3, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0);
static const struct snd_kcontrol_new lamp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 5, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0);
static const struct snd_kcontrol_new hlamp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 10, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 10, 1, 0);
static const struct snd_kcontrol_new hramp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 9, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 9, 1, 0);
static const struct snd_kcontrol_new llamp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 16, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 16, 1, 0);
static const struct snd_kcontrol_new lramp_ctl =
- SOC_DAPM_SINGLE("Switch", 36, 15, 1, 0);
+ SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 15, 1, 0);
static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
/* Input */
SND_SOC_DAPM_INPUT("RXINL"),
SND_SOC_DAPM_INPUT("TXIN"),
- SND_SOC_DAPM_SUPPLY("MC1 Bias", 38, 0, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("MC2 Bias", 38, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MC1 Bias", MC13783_AUDIO_TX, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MC2 Bias", MC13783_AUDIO_TX, 1, 0, NULL, 0),
- SND_SOC_DAPM_SWITCH("MC1L Amp", 38, 7, 0, &mc1l_amp_ctl),
- SND_SOC_DAPM_SWITCH("MC1R Amp", 38, 5, 0, &mc1r_amp_ctl),
- SND_SOC_DAPM_SWITCH("MC2 Amp", 38, 9, 0, &mc2_amp_ctl),
- SND_SOC_DAPM_SWITCH("TXIN Amp", 38, 11, 0, &atx_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC1L Amp", MC13783_AUDIO_TX, 7, 0, &mc1l_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC1R Amp", MC13783_AUDIO_TX, 5, 0, &mc1r_amp_ctl),
+ SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl),
+ SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl),
SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
&left_input_mux),
SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", 40, 11, 0),
- SND_SOC_DAPM_SUPPLY("ADC_Reset", 40, 15, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0),
/* Output */
- SND_SOC_DAPM_SUPPLY("DAC_E", 41, 11, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("DAC_Reset", 41, 15, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("RXOUTL"),
SND_SOC_DAPM_OUTPUT("RXOUTR"),
SND_SOC_DAPM_OUTPUT("HSL"),
SND_SOC_DAPM_OUTPUT("LSP"),
SND_SOC_DAPM_OUTPUT("SP"),
- SND_SOC_DAPM_SWITCH("Speaker Amp", 36, 3, 0, &samp_ctl),
+ SND_SOC_DAPM_SWITCH("Speaker Amp", MC13783_AUDIO_RX0, 3, 0, &samp_ctl),
SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
- SND_SOC_DAPM_SWITCH("Headset Amp Left", 36, 10, 0, &hlamp_ctl),
- SND_SOC_DAPM_SWITCH("Headset Amp Right", 36, 9, 0, &hramp_ctl),
- SND_SOC_DAPM_SWITCH("Line out Amp Left", 36, 16, 0, &llamp_ctl),
- SND_SOC_DAPM_SWITCH("Line out Amp Right", 36, 15, 0, &lramp_ctl),
- SND_SOC_DAPM_DAC("DAC", "Playback", 36, 22, 0),
- SND_SOC_DAPM_PGA("DAC PGA", 37, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0,
+ &hlamp_ctl),
+ SND_SOC_DAPM_SWITCH("Headset Amp Right", MC13783_AUDIO_RX0, 9, 0,
+ &hramp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Left", MC13783_AUDIO_RX0, 16, 0,
+ &llamp_ctl),
+ SND_SOC_DAPM_SWITCH("Line out Amp Right", MC13783_AUDIO_RX0, 15, 0,
+ &lramp_ctl),
+ SND_SOC_DAPM_DAC("DAC", "Playback", MC13783_AUDIO_RX0, 22, 0),
+ SND_SOC_DAPM_PGA("DAC PGA", MC13783_AUDIO_RX1, 5, 0, NULL, 0),
};
static struct snd_soc_dapm_route mc13783_routes[] = {
{
struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
- codec->control_data = priv->mc13xxx;
-
mc13xxx_lock(priv->mc13xxx);
/* these are the reset values */
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
/* Power-up register defaults */
-static const u8 sta32x_regs[STA32X_REGISTER_COUNT] = {
- 0x63, 0x80, 0xc2, 0x40, 0xc2, 0x5c, 0x10, 0xff, 0x60, 0x60,
- 0x60, 0x80, 0x00, 0x00, 0x00, 0x40, 0x80, 0x77, 0x6a, 0x69,
- 0x6a, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
- 0xc0, 0xf3, 0x33, 0x00, 0x0c,
+static const struct reg_default sta32x_regs[] = {
+ { 0x0, 0x63 },
+ { 0x1, 0x80 },
+ { 0x2, 0xc2 },
+ { 0x3, 0x40 },
+ { 0x4, 0xc2 },
+ { 0x5, 0x5c },
+ { 0x6, 0x10 },
+ { 0x7, 0xff },
+ { 0x8, 0x60 },
+ { 0x9, 0x60 },
+ { 0xa, 0x60 },
+ { 0xb, 0x80 },
+ { 0xc, 0x00 },
+ { 0xd, 0x00 },
+ { 0xe, 0x00 },
+ { 0xf, 0x40 },
+ { 0x10, 0x80 },
+ { 0x11, 0x77 },
+ { 0x12, 0x6a },
+ { 0x13, 0x69 },
+ { 0x14, 0x6a },
+ { 0x15, 0x69 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x00 },
+ { 0x19, 0x00 },
+ { 0x1a, 0x00 },
+ { 0x1b, 0x00 },
+ { 0x1c, 0x00 },
+ { 0x1d, 0x00 },
+ { 0x1e, 0x00 },
+ { 0x1f, 0x00 },
+ { 0x20, 0x00 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+ { 0x23, 0x00 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x27, 0x2d },
+ { 0x28, 0xc0 },
+ { 0x2b, 0x00 },
+ { 0x2c, 0x0c },
};
/* regulator power supply names */
/* codec private data */
struct sta32x_priv {
+ struct regmap *regmap;
struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
struct snd_soc_codec *codec;
struct sta32x_platform_data *pdata;
static int sta32x_cache_sync(struct snd_soc_codec *codec)
{
+ struct sta32x_priv *sta32x = codec->control_data;
unsigned int mute;
int rc;
- if (!codec->cache_sync)
- return 0;
-
/* mute during register sync */
mute = snd_soc_read(codec, STA32X_MMUTE);
snd_soc_write(codec, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
sta32x_sync_coef_shadow(codec);
- rc = snd_soc_cache_sync(codec);
+ rc = regcache_sync(sta32x->regmap);
snd_soc_write(codec, STA32X_MMUTE, mute);
return rc;
}
/* check if sta32x has reset itself */
confa_cached = snd_soc_read(codec, STA32X_CONFA);
- codec->cache_bypass = 1;
+ regcache_cache_bypass(sta32x->regmap, true);
confa = snd_soc_read(codec, STA32X_CONFA);
- codec->cache_bypass = 0;
+ regcache_cache_bypass(sta32x->regmap, false);
if (confa != confa_cached) {
- codec->cache_sync = 1;
+ regcache_mark_dirty(sta32x->regmap);
sta32x_cache_sync(codec);
}
sta32x->codec = codec;
sta32x->pdata = dev_get_platdata(codec->dev);
- /* regulators */
- for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
- sta32x->supplies[i].supply = sta32x_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sta32x->supplies),
- sta32x->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- goto err;
- }
-
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
- goto err_get;
+ return ret;
}
/* Tell ASoC what kind of I/O to use to read the registers. ASoC will
* then do the I2C transactions itself.
*/
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ codec->control_data = sta32x->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret);
- return ret;
+ goto err;
}
/* Chip documentation explicitly requires that the reset values
* so the write to the these registers are suppressed by the cache
* restore code when it skips writes of default registers.
*/
- snd_soc_cache_write(codec, STA32X_CONFC, 0xc2);
- snd_soc_cache_write(codec, STA32X_CONFE, 0xc2);
- snd_soc_cache_write(codec, STA32X_CONFF, 0x5c);
- snd_soc_cache_write(codec, STA32X_MMUTE, 0x10);
- snd_soc_cache_write(codec, STA32X_AUTO1, 0x60);
- snd_soc_cache_write(codec, STA32X_AUTO3, 0x00);
- snd_soc_cache_write(codec, STA32X_C3CFG, 0x40);
+ regcache_cache_only(sta32x->regmap, true);
+ snd_soc_write(codec, STA32X_CONFC, 0xc2);
+ snd_soc_write(codec, STA32X_CONFE, 0xc2);
+ snd_soc_write(codec, STA32X_CONFF, 0x5c);
+ snd_soc_write(codec, STA32X_MMUTE, 0x10);
+ snd_soc_write(codec, STA32X_AUTO1, 0x60);
+ snd_soc_write(codec, STA32X_AUTO3, 0x00);
+ snd_soc_write(codec, STA32X_C3CFG, 0x40);
+ regcache_cache_only(sta32x->regmap, false);
/* set thermal warning adjustment and recovery */
if (!(sta32x->pdata->thermal_conf & STA32X_THERMAL_ADJUSTMENT_ENABLE))
return 0;
-err_get:
- regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
err:
+ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
return ret;
}
sta32x_watchdog_stop(sta32x);
sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
- regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
return 0;
}
-static int sta32x_reg_is_volatile(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case STA32X_CONFA ... STA32X_L2ATRT:
.remove = sta32x_remove,
.suspend = sta32x_suspend,
.resume = sta32x_resume,
- .reg_cache_size = STA32X_REGISTER_COUNT,
- .reg_word_size = sizeof(u8),
- .reg_cache_default = sta32x_regs,
- .volatile_register = sta32x_reg_is_volatile,
.set_bias_level = sta32x_set_bias_level,
.controls = sta32x_snd_controls,
.num_controls = ARRAY_SIZE(sta32x_snd_controls),
.num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
};
+static const struct regmap_config sta32x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = STA32X_FDRC2,
+ .reg_defaults = sta32x_regs,
+ .num_reg_defaults = ARRAY_SIZE(sta32x_regs),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = sta32x_reg_is_volatile,
+};
+
static __devinit int sta32x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct sta32x_priv *sta32x;
- int ret;
+ int ret, i;
sta32x = devm_kzalloc(&i2c->dev, sizeof(struct sta32x_priv),
GFP_KERNEL);
if (!sta32x)
return -ENOMEM;
+ /* regulators */
+ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
+ sta32x->supplies[i].supply = sta32x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap);
+ if (IS_ERR(sta32x->regmap)) {
+ ret = PTR_ERR(sta32x->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, sta32x);
ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1);
.id_table = sta32x_i2c_id,
};
-static int __init sta32x_init(void)
-{
- return i2c_add_driver(&sta32x_i2c_driver);
-}
-module_init(sta32x_init);
-
-static void __exit sta32x_exit(void)
-{
- i2c_del_driver(&sta32x_i2c_driver);
-}
-module_exit(sta32x_exit);
+module_i2c_driver(sta32x_i2c_driver);
MODULE_DESCRIPTION("ASoC STA32X driver");
MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
return 0;
}
-struct snd_soc_codec_driver sta529_codec_driver = {
+static const struct snd_soc_codec_driver sta529_codec_driver = {
.probe = sta529_probe,
.remove = sta529_remove,
.set_bias_level = sta529_set_bias_level,
printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
- codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
.remove = aic26_spi_remove,
};
-static int __init aic26_init(void)
-{
- return spi_register_driver(&aic26_spi);
-}
-module_init(aic26_init);
-
-static void __exit aic26_exit(void)
-{
- spi_unregister_driver(&aic26_spi);
-}
-module_exit(aic26_exit);
+module_spi_driver(aic26_spi);
.id_table = aic32x4_i2c_id,
};
-static int __init aic32x4_modinit(void)
-{
- int ret = 0;
-
- ret = i2c_add_driver(&aic32x4_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(aic32x4_modinit);
-
-static void __exit aic32x4_exit(void)
-{
- i2c_del_driver(&aic32x4_i2c_driver);
-}
-module_exit(aic32x4_exit);
+module_i2c_driver(aic32x4_i2c_driver);
MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
{
struct aic3x_pdata *pdata = i2c->dev.platform_data;
struct aic3x_priv *aic3x;
+ struct aic3x_setup_data *ai3x_setup;
+ struct device_node *np = i2c->dev.of_node;
int ret;
aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
if (pdata) {
aic3x->gpio_reset = pdata->gpio_reset;
aic3x->setup = pdata->setup;
+ } else if (np) {
+ ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup),
+ GFP_KERNEL);
+ if (ai3x_setup == NULL) {
+ dev_err(&i2c->dev, "failed to create private data\n");
+ return -ENOMEM;
+ }
+
+ ret = of_get_named_gpio(np, "gpio-reset", 0);
+ if (ret >= 0)
+ aic3x->gpio_reset = ret;
+ else
+ aic3x->gpio_reset = -1;
+
+ if (of_property_read_u32_array(np, "ai3x-gpio-func",
+ ai3x_setup->gpio_func, 2) >= 0) {
+ aic3x->setup = ai3x_setup;
+ }
+
} else {
aic3x->gpio_reset = -1;
}
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id tlv320aic3x_of_match[] = {
+ { .compatible = "ti,tlv320aic3x", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
+#endif
+
/* machine i2c codec control layer */
static struct i2c_driver aic3x_i2c_driver = {
.driver = {
.name = "tlv320aic3x-codec",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tlv320aic3x_of_match),
},
.probe = aic3x_i2c_probe,
.remove = aic3x_i2c_remove,
.id_table = aic3x_i2c_id,
};
-static int __init aic3x_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&aic3x_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register TLV320AIC3x I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(aic3x_modinit);
-
-static void __exit aic3x_exit(void)
-{
- i2c_del_driver(&aic3x_i2c_driver);
-}
-module_exit(aic3x_exit);
+module_i2c_driver(aic3x_i2c_driver);
MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
MODULE_AUTHOR("Vladimir Barinov");
.id_table = tlv320dac33_i2c_id,
};
-static int __init dac33_module_init(void)
-{
- int r;
- r = i2c_add_driver(&tlv320dac33_i2c_driver);
- if (r < 0) {
- printk(KERN_ERR "DAC33: driver registration failed\n");
- return r;
- }
- return 0;
-}
-module_init(dac33_module_init);
-
-static void __exit dac33_module_exit(void)
-{
- i2c_del_driver(&tlv320dac33_i2c_driver);
-}
-module_exit(dac33_module_exit);
-
+module_i2c_driver(tlv320dac33_i2c_driver);
MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
.id_table = tpa6130a2_id,
};
-static int __init tpa6130a2_init(void)
-{
- return i2c_add_driver(&tpa6130a2_i2c_driver);
-}
-
-static void __exit tpa6130a2_exit(void)
-{
- i2c_del_driver(&tpa6130a2_i2c_driver);
-}
+module_i2c_driver(tpa6130a2_i2c_driver);
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL");
-
-module_init(tpa6130a2_init);
-module_exit(tpa6130a2_exit);
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/i2c/twl.h>
#include <linux/slab.h>
+#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
u8 predrivel_enabled, predriver_enabled;
u8 carkitl_enabled, carkitr_enabled;
- /* Delay needed after enabling the digimic interface */
- unsigned int digimic_delay;
+ struct twl4030_codec_data *pdata;
};
/*
}
-static void twl4030_init_chip(struct snd_soc_codec *codec)
+static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata,
+ struct device_node *node)
+{
+ int value;
+
+ of_property_read_u32(node, "ti,digimic_delay",
+ &pdata->digimic_delay);
+ of_property_read_u32(node, "ti,ramp_delay_value",
+ &pdata->ramp_delay_value);
+ of_property_read_u32(node, "ti,offset_cncl_path",
+ &pdata->offset_cncl_path);
+ if (!of_property_read_u32(node, "ti,hs_extmute", &value))
+ pdata->hs_extmute = value;
+
+ pdata->hs_extmute_gpio = of_get_named_gpio(node,
+ "ti,hs_extmute_gpio", 0);
+ if (gpio_is_valid(pdata->hs_extmute_gpio))
+ pdata->hs_extmute = 1;
+}
+
+static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec)
{
struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
+ struct device_node *twl4030_codec_node = NULL;
+
+ twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node,
+ "codec");
+
+ if (!pdata && twl4030_codec_node) {
+ pdata = devm_kzalloc(codec->dev,
+ sizeof(struct twl4030_codec_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(codec->dev, "Can not allocate memory\n");
+ return NULL;
+ }
+ twl4030_setup_pdata_of(pdata, twl4030_codec_node);
+ }
+
+ return pdata;
+}
+
+static void twl4030_init_chip(struct snd_soc_codec *codec)
+{
+ struct twl4030_codec_data *pdata;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 reg, byte;
int i = 0;
+ pdata = twl4030_get_pdata(codec);
+
+ if (pdata && pdata->hs_extmute &&
+ gpio_is_valid(pdata->hs_extmute_gpio)) {
+ int ret;
+
+ if (!pdata->hs_extmute_gpio)
+ dev_warn(codec->dev,
+ "Extmute GPIO is 0 is this correct?\n");
+
+ ret = gpio_request_one(pdata->hs_extmute_gpio,
+ GPIOF_OUT_INIT_LOW, "hs_extmute");
+ if (ret) {
+ dev_err(codec->dev, "Failed to get hs_extmute GPIO\n");
+ pdata->hs_extmute_gpio = -1;
+ }
+ }
+
/* Check defaults, if instructed before anything else */
if (pdata && pdata->check_defaults)
twl4030_check_defaults(codec);
if (!pdata)
return;
- twl4030->digimic_delay = pdata->digimic_delay;
+ twl4030->pdata = pdata;
reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
reg &= ~TWL4030_RAMP_DELAY;
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
- struct twl4030_codec_data *pdata = codec->dev->platform_data;
unsigned char hs_gain, hs_pop;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
/* Base values for ramp delay calculation: 2^19 - 2^26 */
unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
8388608, 16777216, 33554432, 67108864};
/* Enable external mute control, this dramatically reduces
* the pop-noise */
if (pdata && pdata->hs_extmute) {
- if (pdata->set_hs_extmute) {
- pdata->set_hs_extmute(1);
+ if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+ gpio_set_value(pdata->hs_extmute_gpio, 1);
} else {
hs_pop |= TWL4030_EXTMUTE;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Disable external mute */
if (pdata && pdata->hs_extmute) {
- if (pdata->set_hs_extmute) {
- pdata->set_hs_extmute(0);
+ if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+ gpio_set_value(pdata->hs_extmute_gpio, 0);
} else {
hs_pop &= ~TWL4030_EXTMUTE;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
struct snd_kcontrol *kcontrol, int event)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
- if (twl4030->digimic_delay)
- twl4030_wait_ms(twl4030->digimic_delay);
+ if (pdata && pdata->digimic_delay)
+ twl4030_wait_ms(pdata->digimic_delay);
return 0;
}
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned short val;
- unsigned short mask, bitmask;
+ unsigned short mask;
if (twl4030->configured) {
dev_err(codec->dev,
return -EBUSY;
}
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
val = ucontrol->value.enumerated.item[0] << e->shift_l;
- mask = (bitmask - 1) << e->shift_l;
+ mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
- mask |= (bitmask - 1) << e->shift_r;
+ mask |= e->mask << e->shift_r;
}
return snd_soc_update_bits(codec, e->reg, mask, val);
SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"),
/* DACs */
- SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback",
- SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Left1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Right2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0),
/* Analog bypasses */
SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
/* Introducing four virtual ADC, since TWL4030 have four channel for
capture */
- SND_SOC_DAPM_ADC("ADC Virtual Left1", "Left Front Capture",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_ADC("ADC Virtual Right1", "Right Front Capture",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_ADC("ADC Virtual Left2", "Left Rear Capture",
- SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_ADC("ADC Virtual Right2", "Right Rear Capture",
- SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Left1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Right1", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0),
/* Analog/Digital mic path selection.
TX1 Left/Right: either analog Left/Right or Digimic0
};
static const struct snd_soc_dapm_route intercon[] = {
+ /* Stream -> DAC mapping */
+ {"DAC Right1", NULL, "HiFi Playback"},
+ {"DAC Left1", NULL, "HiFi Playback"},
+ {"DAC Right2", NULL, "HiFi Playback"},
+ {"DAC Left2", NULL, "HiFi Playback"},
+ {"DAC Voice", NULL, "Voice Playback"},
+
+ /* ADC -> Stream mapping */
+ {"HiFi Capture", NULL, "ADC Virtual Left1"},
+ {"HiFi Capture", NULL, "ADC Virtual Right1"},
+ {"HiFi Capture", NULL, "ADC Virtual Left2"},
+ {"HiFi Capture", NULL, "ADC Virtual Right2"},
+ {"Voice Capture", NULL, "ADC Virtual Left1"},
+ {"Voice Capture", NULL, "ADC Virtual Right1"},
+ {"Voice Capture", NULL, "ADC Virtual Left2"},
+ {"Voice Capture", NULL, "ADC Virtual Right2"},
+
{"Digital L1 Playback Mixer", NULL, "DAC Left1"},
{"Digital R1 Playback Mixer", NULL, "DAC Right1"},
{"Digital L2 Playback Mixer", NULL, "DAC Left2"},
.formats = TWL4030_FORMATS,
.sig_bits = 24,},
.capture = {
- .stream_name = "Capture",
+ .stream_name = "HiFi Capture",
.channels_min = 2,
.channels_max = 4,
.rates = TWL4030_RATES,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
- .stream_name = "Capture",
+ .stream_name = "Voice Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
{
struct twl4030_priv *twl4030;
- twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
+ twl4030 = devm_kzalloc(codec->dev, sizeof(struct twl4030_priv),
+ GFP_KERNEL);
if (twl4030 == NULL) {
dev_err(codec->dev, "Can not allocate memory\n");
return -ENOMEM;
static int twl4030_soc_remove(struct snd_soc_codec *codec)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+ struct twl4030_codec_data *pdata = twl4030->pdata;
/* Reset registers to their chip default before leaving */
twl4030_reset_registers(codec);
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
- kfree(twl4030);
+
+ if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
+ gpio_free(pdata->hs_extmute_gpio);
+
return 0;
}
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
- struct twl4030_codec_data *pdata = pdev->dev.platform_data;
-
- if (!pdata) {
- dev_err(&pdev->dev, "platform_data is missing\n");
- return -EINVAL;
- }
-
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030,
twl4030_dai, ARRAY_SIZE(twl4030_dai));
}
TWL6040_REG_MICRCTL, 1, 0, NULL, 0),
/* ADCs */
- SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture",
- TWL6040_REG_MICLCTL, 2, 0),
- SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture",
- TWL6040_REG_MICRCTL, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Left", NULL, TWL6040_REG_MICLCTL, 2, 0),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, TWL6040_REG_MICRCTL, 2, 0),
/* Microphone bias */
SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
TWL6040_REG_DMICBCTL, 4, 0, NULL, 0),
/* DACs */
- SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback", SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback", SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_DAC("HFDAC Left", "Handsfree Playback",
- TWL6040_REG_HFLCTL, 0, 0),
- SND_SOC_DAPM_DAC("HFDAC Right", "Handsfree Playback",
- TWL6040_REG_HFRCTL, 0, 0),
+ SND_SOC_DAPM_DAC("HSDAC Left", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HSDAC Right", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("HFDAC Left", NULL, TWL6040_REG_HFLCTL, 0, 0),
+ SND_SOC_DAPM_DAC("HFDAC Right", NULL, TWL6040_REG_HFRCTL, 0, 0),
/* Virtual DAC for vibra path (DL4 channel) */
- SND_SOC_DAPM_DAC("VIBRA DAC", "Vibra Playback",
- SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("VIBRA DAC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("Handsfree Left Playback",
SND_SOC_NOPM, 0, 0, &hfl_mux_controls),
};
static const struct snd_soc_dapm_route intercon[] = {
+ /* Stream -> DAC mapping */
+ {"HSDAC Left", NULL, "Legacy Playback"},
+ {"HSDAC Left", NULL, "Headset Playback"},
+ {"HSDAC Right", NULL, "Legacy Playback"},
+ {"HSDAC Right", NULL, "Headset Playback"},
+
+ {"HFDAC Left", NULL, "Legacy Playback"},
+ {"HFDAC Left", NULL, "Handsfree Playback"},
+ {"HFDAC Right", NULL, "Legacy Playback"},
+ {"HFDAC Right", NULL, "Handsfree Playback"},
+
+ {"VIBRA DAC", NULL, "Legacy Playback"},
+ {"VIBRA DAC", NULL, "Vibra Playback"},
+
+ /* ADC -> Stream mapping */
+ {"ADC Left", NULL, "Legacy Capture"},
+ {"ADC Left", NULL, "Capture"},
+ {"ADC Right", NULL, "Legacy Capture"},
+ {"ADC Right", NULL, "Capture"},
+
/* Capture path */
{"Analog Left Capture Route", "Headset Mic", "HSMIC"},
{"Analog Left Capture Route", "Main Mic", "MAINMIC"},
{
.name = "twl6040-legacy",
.playback = {
- .stream_name = "Playback",
+ .stream_name = "Legacy Playback",
.channels_min = 1,
.channels_max = 5,
.rates = TWL6040_RATES,
.formats = TWL6040_FORMATS,
},
.capture = {
- .stream_name = "Capture",
+ .stream_name = "Legacy Capture",
.channels_min = 1,
.channels_max = 2,
.rates = TWL6040_RATES,
--- /dev/null
+/*
+ * wm0010.c -- WM0010 DSP Driver
+ *
+ * Copyright 2012 Wolfson Microelectronics PLC.
+ *
+ * Authors: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
+ * Scott Ling <sl@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/irqreturn.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <sound/soc.h>
+#include <sound/wm0010.h>
+
+#define DEVICE_ID_WM0010 10
+
+enum dfw_cmd {
+ DFW_CMD_FUSE = 0x01,
+ DFW_CMD_CODE_HDR,
+ DFW_CMD_CODE_DATA,
+ DFW_CMD_PLL,
+ DFW_CMD_INFO = 0xff
+};
+
+struct dfw_binrec {
+ u8 command;
+ u32 length:24;
+ u32 address;
+ uint8_t data[0];
+} __packed;
+
+struct dfw_pllrec {
+ u8 command;
+ u32 length:24;
+ u32 address;
+ u32 clkctrl1;
+ u32 clkctrl2;
+ u32 clkctrl3;
+ u32 ldetctrl;
+ u32 uart_div;
+ u32 spi_div;
+} __packed;
+
+static struct pll_clock_map {
+ int max_sysclk;
+ int max_pll_spi_speed;
+ u32 pll_clkctrl1;
+} pll_clock_map[] = { /* Dividers */
+ { 22000000, 26000000, 0x00201f11 }, /* 2,32,2 */
+ { 18000000, 26000000, 0x00203f21 }, /* 2,64,4 */
+ { 14000000, 26000000, 0x00202620 }, /* 1,39,4 */
+ { 10000000, 22000000, 0x00203120 }, /* 1,50,4 */
+ { 6500000, 22000000, 0x00204520 }, /* 1,70,4 */
+ { 5500000, 22000000, 0x00103f10 }, /* 1,64,2 */
+};
+
+enum wm0010_state {
+ WM0010_POWER_OFF,
+ WM0010_OUT_OF_RESET,
+ WM0010_BOOTROM,
+ WM0010_STAGE2,
+ WM0010_FIRMWARE,
+};
+
+struct wm0010_priv {
+ struct snd_soc_codec *codec;
+
+ struct mutex lock;
+ struct device *dev;
+
+ struct wm0010_pdata pdata;
+
+ int gpio_reset;
+ int gpio_reset_value;
+
+ struct regulator_bulk_data core_supplies[2];
+ struct regulator *dbvdd;
+
+ int sysclk;
+
+ enum wm0010_state state;
+ bool boot_failed;
+ int boot_done;
+ bool ready;
+ bool pll_running;
+ int max_spi_freq;
+ int board_max_spi_speed;
+ u32 pll_clkctrl1;
+
+ spinlock_t irq_lock;
+ int irq;
+
+ struct completion boot_completion;
+};
+
+struct wm0010_spi_msg {
+ struct spi_message m;
+ struct spi_transfer t;
+ u8 *tx_buf;
+ u8 *rx_buf;
+ size_t len;
+};
+
+static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route wm0010_dapm_routes[] = {
+ { "SDI2 Capture", NULL, "SDI1 Playback" },
+ { "SDI1 Capture", NULL, "SDI2 Playback" },
+
+ { "SDI1 Capture", NULL, "CLKIN" },
+ { "SDI2 Capture", NULL, "CLKIN" },
+ { "SDI1 Playback", NULL, "CLKIN" },
+ { "SDI2 Playback", NULL, "CLKIN" },
+};
+
+static const char *wm0010_state_to_str(enum wm0010_state state)
+{
+ const char *state_to_str[] = {
+ "Power off",
+ "Out of reset",
+ "Boot ROM",
+ "Stage2",
+ "Firmware"
+ };
+
+ if (state < 0 || state >= ARRAY_SIZE(state_to_str))
+ return "null";
+ return state_to_str[state];
+}
+
+/* Called with wm0010->lock held */
+static void wm0010_halt(struct snd_soc_codec *codec)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned long flags;
+ enum wm0010_state state;
+
+ /* Fetch the wm0010 state */
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ state = wm0010->state;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ switch (state) {
+ case WM0010_POWER_OFF:
+ /* If there's nothing to do, bail out */
+ return;
+ case WM0010_OUT_OF_RESET:
+ case WM0010_BOOTROM:
+ case WM0010_STAGE2:
+ case WM0010_FIRMWARE:
+ /* Remember to put chip back into reset */
+ gpio_set_value_cansleep(wm0010->gpio_reset,
+ wm0010->gpio_reset_value);
+ /* Disable the regulators */
+ regulator_disable(wm0010->dbvdd);
+ regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ break;
+ }
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_POWER_OFF;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+}
+
+struct wm0010_boot_xfer {
+ struct list_head list;
+ struct snd_soc_codec *codec;
+ struct completion *done;
+ struct spi_message m;
+ struct spi_transfer t;
+};
+
+/* Called with wm0010->lock held */
+static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010)
+{
+ enum wm0010_state state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ state = wm0010->state;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n",
+ wm0010_state_to_str(state), wm0010_state_to_str(state + 1));
+
+ wm0010->boot_failed = true;
+}
+
+static void wm0010_boot_xfer_complete(void *data)
+{
+ struct wm0010_boot_xfer *xfer = data;
+ struct snd_soc_codec *codec = xfer->codec;
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ u32 *out32 = xfer->t.rx_buf;
+ int i;
+
+ if (xfer->m.status != 0) {
+ dev_err(codec->dev, "SPI transfer failed: %d\n",
+ xfer->m.status);
+ wm0010_mark_boot_failure(wm0010);
+ if (xfer->done)
+ complete(xfer->done);
+ return;
+ }
+
+ for (i = 0; i < xfer->t.len / 4; i++) {
+ dev_dbg(codec->dev, "%d: %04x\n", i, out32[i]);
+
+ switch (be32_to_cpu(out32[i])) {
+ case 0xe0e0e0e0:
+ dev_err(codec->dev,
+ "%d: ROM error reported in stage 2\n", i);
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x55555555:
+ if (wm0010->boot_done == 0)
+ break;
+ dev_err(codec->dev,
+ "%d: ROM bootloader running in stage 2\n", i);
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0000:
+ dev_dbg(codec->dev, "Stage2 loader running\n");
+ break;
+
+ case 0x0fed0007:
+ dev_dbg(codec->dev, "CODE_HDR packet received\n");
+ break;
+
+ case 0x0fed0008:
+ dev_dbg(codec->dev, "CODE_DATA packet received\n");
+ break;
+
+ case 0x0fed0009:
+ dev_dbg(codec->dev, "Download complete\n");
+ break;
+
+ case 0x0fed000c:
+ dev_dbg(codec->dev, "Application start\n");
+ break;
+
+ case 0x0fed000e:
+ dev_dbg(codec->dev, "PLL packet received\n");
+ wm0010->pll_running = true;
+ break;
+
+ case 0x0fed0025:
+ dev_err(codec->dev, "Device reports image too long\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed002c:
+ dev_err(codec->dev, "Device reports bad SPI packet\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0031:
+ dev_err(codec->dev, "Device reports SPI read overflow\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0032:
+ dev_err(codec->dev, "Device reports SPI underclock\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0033:
+ dev_err(codec->dev, "Device reports bad header packet\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0034:
+ dev_err(codec->dev, "Device reports invalid packet type\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0035:
+ dev_err(codec->dev, "Device reports data before header error\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ case 0x0fed0038:
+ dev_err(codec->dev, "Device reports invalid PLL packet\n");
+ break;
+
+ case 0x0fed003a:
+ dev_err(codec->dev, "Device reports packet alignment error\n");
+ wm0010_mark_boot_failure(wm0010);
+ break;
+
+ default:
+ dev_err(codec->dev, "Unrecognised return 0x%x\n",
+ be32_to_cpu(out32[i]));
+ wm0010_mark_boot_failure(wm0010);
+ break;
+ }
+
+ if (wm0010->boot_failed)
+ break;
+ }
+
+ wm0010->boot_done++;
+ if (xfer->done)
+ complete(xfer->done);
+}
+
+static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len)
+{
+ int i;
+
+ for (i = 0; i < len / 8; i++)
+ data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i]));
+}
+
+static int wm0010_boot(struct snd_soc_codec *codec)
+{
+ struct spi_device *spi = to_spi_device(codec->dev);
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned long flags;
+ struct list_head xfer_list;
+ struct wm0010_boot_xfer *xfer;
+ int ret;
+ struct completion done;
+ const struct firmware *fw;
+ const struct dfw_binrec *rec;
+ struct spi_message m;
+ struct spi_transfer t;
+ struct dfw_pllrec pll_rec;
+ u32 *img, *p;
+ u64 *img_swap;
+ u8 *out;
+ u32 len, offset;
+ int i;
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ if (wm0010->state != WM0010_POWER_OFF)
+ dev_warn(wm0010->dev, "DSP already powered up!\n");
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ if (wm0010->sysclk > 26000000) {
+ dev_err(codec->dev, "Max DSP clock frequency is 26MHz\n");
+ ret = -ECANCELED;
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&xfer_list);
+
+ mutex_lock(&wm0010->lock);
+ wm0010->pll_running = false;
+
+ dev_dbg(codec->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to enable core supplies: %d\n",
+ ret);
+ mutex_unlock(&wm0010->lock);
+ goto err;
+ }
+
+ ret = regulator_enable(wm0010->dbvdd);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to enable DBVDD: %d\n", ret);
+ goto err_core;
+ }
+
+ /* Release reset */
+ gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value);
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_OUT_OF_RESET;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ /* First the bootloader */
+ ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request stage2 loader: %d\n",
+ ret);
+ goto abort;
+ }
+
+ if (!wait_for_completion_timeout(&wm0010->boot_completion,
+ msecs_to_jiffies(10)))
+ dev_err(codec->dev, "Failed to get interrupt from DSP\n");
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_BOOTROM;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
+
+ /* Copy to local buffer first as vmalloc causes problems for dma */
+ img = kzalloc(fw->size, GFP_KERNEL);
+ if (!img) {
+ dev_err(codec->dev, "Failed to allocate image buffer\n");
+ goto abort;
+ }
+
+ out = kzalloc(fw->size, GFP_KERNEL);
+ if (!out) {
+ dev_err(codec->dev, "Failed to allocate output buffer\n");
+ goto abort;
+ }
+
+ memcpy(img, &fw->data[0], fw->size);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+ t.rx_buf = out;
+ t.tx_buf = img;
+ t.len = fw->size;
+ t.bits_per_word = 8;
+ t.speed_hz = wm0010->sysclk / 10;
+ spi_message_add_tail(&t, &m);
+
+ dev_dbg(codec->dev, "Starting initial download at %dHz\n",
+ t.speed_hz);
+
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Initial download failed: %d\n", ret);
+ goto abort;
+ }
+
+ /* Look for errors from the boot ROM */
+ for (i = 0; i < fw->size; i++) {
+ if (out[i] != 0x55) {
+ ret = -EBUSY;
+ dev_err(codec->dev, "Boot ROM error: %x in %d\n",
+ out[i], i);
+ wm0010_mark_boot_failure(wm0010);
+ goto abort;
+ }
+ }
+
+ release_firmware(fw);
+ kfree(img);
+ kfree(out);
+
+ if (!wait_for_completion_timeout(&wm0010->boot_completion,
+ msecs_to_jiffies(10)))
+ dev_err(codec->dev, "Failed to get interrupt from DSP loader.\n");
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_STAGE2;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ /* Only initialise PLL if max_spi_freq initialised */
+ if (wm0010->max_spi_freq) {
+
+ /* Initialise a PLL record */
+ memset(&pll_rec, 0, sizeof(pll_rec));
+ pll_rec.command = DFW_CMD_PLL;
+ pll_rec.length = (sizeof(pll_rec) - 8);
+
+ /* On wm0010 only the CLKCTRL1 value is used */
+ pll_rec.clkctrl1 = wm0010->pll_clkctrl1;
+
+ len = pll_rec.length + 8;
+ out = kzalloc(len, GFP_KERNEL);
+ if (!out) {
+ dev_err(codec->dev,
+ "Failed to allocate RX buffer\n");
+ goto abort;
+ }
+
+ img_swap = kzalloc(len, GFP_KERNEL);
+ if (!img_swap) {
+ dev_err(codec->dev,
+ "Failed to allocate image buffer\n");
+ goto abort;
+ }
+
+ /* We need to re-order for 0010 */
+ byte_swap_64((u64 *)&pll_rec, img_swap, len);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+ t.rx_buf = out;
+ t.tx_buf = img_swap;
+ t.len = len;
+ t.bits_per_word = 8;
+ t.speed_hz = wm0010->sysclk / 6;
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "First PLL write failed: %d\n", ret);
+ goto abort;
+ }
+
+ /* Use a second send of the message to get the return status */
+ ret = spi_sync(spi, &m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Second PLL write failed: %d\n", ret);
+ goto abort;
+ }
+
+ p = (u32 *)out;
+
+ /* Look for PLL active code from the DSP */
+ for (i = 0; i < len / 4; i++) {
+ if (*p == 0x0e00ed0f) {
+ dev_dbg(codec->dev, "PLL packet received\n");
+ wm0010->pll_running = true;
+ break;
+ }
+ p++;
+ }
+
+ kfree(img_swap);
+ kfree(out);
+ } else
+ dev_dbg(codec->dev, "Not enabling DSP PLL.");
+
+ ret = request_firmware(&fw, "wm0010.dfw", codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request application: %d\n",
+ ret);
+ goto abort;
+ }
+
+ rec = (const struct dfw_binrec *)fw->data;
+ offset = 0;
+ wm0010->boot_done = 0;
+ wm0010->boot_failed = false;
+ BUG_ON(!list_empty(&xfer_list));
+ init_completion(&done);
+
+ /* First record should be INFO */
+ if (rec->command != DFW_CMD_INFO) {
+ dev_err(codec->dev, "First record not INFO\r\n");
+ goto abort;
+ }
+
+ /* Check it's a 0010 file */
+ if (rec->data[0] != DEVICE_ID_WM0010) {
+ dev_err(codec->dev, "Not a WM0010 firmware file.\r\n");
+ goto abort;
+ }
+
+ /* Skip the info record as we don't need to send it */
+ offset += ((rec->length) + 8);
+ rec = (void *)&rec->data[rec->length];
+
+ while (offset < fw->size) {
+ dev_dbg(codec->dev,
+ "Packet: command %d, data length = 0x%x\r\n",
+ rec->command, rec->length);
+ len = rec->length + 8;
+
+ out = kzalloc(len, GFP_KERNEL);
+ if (!out) {
+ dev_err(codec->dev,
+ "Failed to allocate RX buffer\n");
+ goto abort;
+ }
+
+ img_swap = kzalloc(len, GFP_KERNEL);
+ if (!img_swap) {
+ dev_err(codec->dev,
+ "Failed to allocate image buffer\n");
+ goto abort;
+ }
+
+ /* We need to re-order for 0010 */
+ byte_swap_64((u64 *)&rec->command, img_swap, len);
+
+ xfer = kzalloc(sizeof(*xfer), GFP_KERNEL);
+ if (!xfer) {
+ dev_err(codec->dev, "Failed to allocate xfer\n");
+ goto abort;
+ }
+
+ xfer->codec = codec;
+ list_add_tail(&xfer->list, &xfer_list);
+
+ spi_message_init(&xfer->m);
+ xfer->m.complete = wm0010_boot_xfer_complete;
+ xfer->m.context = xfer;
+ xfer->t.tx_buf = img_swap;
+ xfer->t.rx_buf = out;
+ xfer->t.len = len;
+ xfer->t.bits_per_word = 8;
+
+ if (!wm0010->pll_running) {
+ xfer->t.speed_hz = wm0010->sysclk / 6;
+ } else {
+ xfer->t.speed_hz = wm0010->max_spi_freq;
+
+ if (wm0010->board_max_spi_speed &&
+ (wm0010->board_max_spi_speed < wm0010->max_spi_freq))
+ xfer->t.speed_hz = wm0010->board_max_spi_speed;
+ }
+
+ /* Store max usable spi frequency for later use */
+ wm0010->max_spi_freq = xfer->t.speed_hz;
+
+ spi_message_add_tail(&xfer->t, &xfer->m);
+
+ offset += ((rec->length) + 8);
+ rec = (void *)&rec->data[rec->length];
+
+ if (offset >= fw->size) {
+ dev_dbg(codec->dev, "All transfers scheduled\n");
+ xfer->done = &done;
+ }
+
+ ret = spi_async(spi, &xfer->m);
+ if (ret != 0) {
+ dev_err(codec->dev, "Write failed: %d\n", ret);
+ goto abort;
+ }
+
+ if (wm0010->boot_failed)
+ goto abort;
+ }
+
+ wait_for_completion(&done);
+
+ spin_lock_irqsave(&wm0010->irq_lock, flags);
+ wm0010->state = WM0010_FIRMWARE;
+ spin_unlock_irqrestore(&wm0010->irq_lock, flags);
+
+ mutex_unlock(&wm0010->lock);
+
+ release_firmware(fw);
+
+ while (!list_empty(&xfer_list)) {
+ xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer,
+ list);
+ kfree(xfer->t.rx_buf);
+ kfree(xfer->t.tx_buf);
+ list_del(&xfer->list);
+ kfree(xfer);
+ }
+
+ return 0;
+
+abort:
+ /* Put the chip back into reset */
+ wm0010_halt(codec);
+ mutex_unlock(&wm0010->lock);
+ return ret;
+
+err_core:
+ mutex_unlock(&wm0010->lock);
+ regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+err:
+ return ret;
+}
+
+static int wm0010_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE)
+ wm0010_boot(codec);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+ mutex_lock(&wm0010->lock);
+ wm0010_halt(codec);
+ mutex_unlock(&wm0010->lock);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+ unsigned int i;
+
+ wm0010->sysclk = freq;
+
+ if (freq < pll_clock_map[ARRAY_SIZE(pll_clock_map)-1].max_sysclk) {
+ wm0010->max_spi_freq = 0;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++)
+ if (freq >= pll_clock_map[i].max_sysclk)
+ break;
+
+ wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed;
+ wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1;
+ }
+
+ return 0;
+}
+
+static int wm0010_probe(struct snd_soc_codec *codec);
+
+static struct snd_soc_codec_driver soc_codec_dev_wm0010 = {
+ .probe = wm0010_probe,
+ .set_bias_level = wm0010_set_bias_level,
+ .set_sysclk = wm0010_set_sysclk,
+ .idle_bias_off = true,
+
+ .dapm_widgets = wm0010_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets),
+ .dapm_routes = wm0010_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes),
+};
+
+#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm0010_dai[] = {
+ {
+ .name = "wm0010-sdi1",
+ .playback = {
+ .stream_name = "SDI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ },
+ {
+ .name = "wm0010-sdi2",
+ .playback = {
+ .stream_name = "SDI2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDI2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM0010_RATES,
+ .formats = WM0010_FORMATS,
+ },
+ },
+};
+
+static irqreturn_t wm0010_irq(int irq, void *data)
+{
+ struct wm0010_priv *wm0010 = data;
+
+ switch (wm0010->state) {
+ case WM0010_POWER_OFF:
+ case WM0010_OUT_OF_RESET:
+ case WM0010_BOOTROM:
+ case WM0010_STAGE2:
+ spin_lock(&wm0010->irq_lock);
+ complete(&wm0010->boot_completion);
+ spin_unlock(&wm0010->irq_lock);
+ return IRQ_HANDLED;
+ default:
+ return IRQ_NONE;
+ }
+
+ return IRQ_NONE;
+}
+
+static int wm0010_probe(struct snd_soc_codec *codec)
+{
+ struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
+
+ wm0010->codec = codec;
+
+ return 0;
+}
+
+static int __devinit wm0010_spi_probe(struct spi_device *spi)
+{
+ unsigned long gpio_flags;
+ int ret;
+ int trigger;
+ int irq;
+ struct wm0010_priv *wm0010;
+
+ wm0010 = devm_kzalloc(&spi->dev, sizeof(*wm0010),
+ GFP_KERNEL);
+ if (!wm0010)
+ return -ENOMEM;
+
+ mutex_init(&wm0010->lock);
+ spin_lock_init(&wm0010->irq_lock);
+
+ spi_set_drvdata(spi, wm0010);
+ wm0010->dev = &spi->dev;
+
+ if (dev_get_platdata(&spi->dev))
+ memcpy(&wm0010->pdata, dev_get_platdata(&spi->dev),
+ sizeof(wm0010->pdata));
+
+ init_completion(&wm0010->boot_completion);
+
+ wm0010->core_supplies[0].supply = "AVDD";
+ wm0010->core_supplies[1].supply = "DCVDD";
+ ret = devm_regulator_bulk_get(wm0010->dev, ARRAY_SIZE(wm0010->core_supplies),
+ wm0010->core_supplies);
+ if (ret != 0) {
+ dev_err(wm0010->dev, "Failed to obtain core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ wm0010->dbvdd = devm_regulator_get(wm0010->dev, "DBVDD");
+ if (IS_ERR(wm0010->dbvdd)) {
+ ret = PTR_ERR(wm0010->dbvdd);
+ dev_err(wm0010->dev, "Failed to obtain DBVDD: %d\n", ret);
+ return ret;
+ }
+
+ if (wm0010->pdata.gpio_reset) {
+ wm0010->gpio_reset = wm0010->pdata.gpio_reset;
+
+ if (wm0010->pdata.reset_active_high)
+ wm0010->gpio_reset_value = 1;
+ else
+ wm0010->gpio_reset_value = 0;
+
+ if (wm0010->gpio_reset_value)
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset,
+ gpio_flags, "wm0010 reset");
+ if (ret < 0) {
+ dev_err(wm0010->dev,
+ "Failed to request GPIO for DSP reset: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ dev_err(wm0010->dev, "No reset GPIO configured\n");
+ return -EINVAL;
+ }
+
+ wm0010->state = WM0010_POWER_OFF;
+
+ irq = spi->irq;
+ if (wm0010->pdata.irq_flags)
+ trigger = wm0010->pdata.irq_flags;
+ else
+ trigger = IRQF_TRIGGER_FALLING;
+ trigger |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger | IRQF_ONESHOT,
+ "wm0010", wm0010);
+ if (ret) {
+ dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+ wm0010->irq = irq;
+
+ if (spi->max_speed_hz)
+ wm0010->board_max_spi_speed = spi->max_speed_hz;
+ else
+ wm0010->board_max_spi_speed = 0;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_wm0010, wm0010_dai,
+ ARRAY_SIZE(wm0010_dai));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int __devexit wm0010_spi_remove(struct spi_device *spi)
+{
+ struct wm0010_priv *wm0010 = spi_get_drvdata(spi);
+
+ snd_soc_unregister_codec(&spi->dev);
+
+ gpio_set_value_cansleep(wm0010->gpio_reset,
+ wm0010->gpio_reset_value);
+
+ if (wm0010->irq)
+ free_irq(wm0010->irq, wm0010);
+
+ return 0;
+}
+
+static struct spi_driver wm0010_spi_driver = {
+ .driver = {
+ .name = "wm0010",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm0010_spi_probe,
+ .remove = __devexit_p(wm0010_spi_remove),
+};
+
+module_spi_driver(wm0010_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM0010 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "wm2000.h"
+#define WM2000_NUM_SUPPLIES 3
+
+static const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = {
+ "SPKVDD",
+ "DBVDD",
+ "DCVDD",
+};
+
enum wm2000_anc_mode {
ANC_ACTIVE = 0,
ANC_BYPASS = 1,
struct i2c_client *i2c;
struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES];
+
enum wm2000_anc_mode anc_mode;
unsigned int anc_active:1;
dev_dbg(&i2c->dev, "Beginning power up\n");
+ ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
if (!wm2000->mclk_div) {
dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
wm2000_write(i2c, WM2000_REG_SYS_CTL2,
if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
WM2000_ANC_ENG_IDLE)) {
dev_err(&i2c->dev, "ANC engine failed to reset\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_BOOT_COMPLETE)) {
dev_err(&i2c->dev, "ANC engine failed to initialise\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
return -ETIMEDOUT;
}
wm2000->anc_download_size);
if (ret < 0) {
dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret);
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
return ret;
}
if (ret != wm2000->anc_download_size) {
dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n",
ret, wm2000->anc_download_size);
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
return -EIO;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_MOUSE_ACTIVE)) {
dev_err(&i2c->dev, "Timed out waiting for device\n");
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
return -ETIMEDOUT;
}
return -ETIMEDOUT;
}
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+
dev_dbg(&i2c->dev, "powered off\n");
wm2000->anc_mode = ANC_OFF;
struct wm2000_platform_data *pdata;
const char *filename;
const struct firmware *fw = NULL;
- int ret;
+ int ret, i;
int reg;
u16 id;
dev_set_drvdata(&i2c->dev, wm2000);
- wm2000->regmap = regmap_init_i2c(i2c, &wm2000_regmap);
+ wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap);
if (IS_ERR(wm2000->regmap)) {
ret = PTR_ERR(wm2000->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
goto out;
}
+ for (i = 0; i < WM2000_NUM_SUPPLIES; i++)
+ wm2000->supplies[i].supply = wm2000_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES,
+ wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
/* Verify that this is a WM2000 */
reg = wm2000_read(i2c, WM2000_REG_ID1);
id = reg << 8;
if (id != 0x2000) {
dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
ret = -ENODEV;
- goto out_regmap_exit;
+ goto err_supplies;
}
reg = wm2000_read(i2c, WM2000_REG_REVISON);
ret = request_firmware(&fw, filename, &i2c->dev);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
- goto out_regmap_exit;
+ goto err_supplies;
}
/* Pre-cook the concatenation of the register address onto the image */
if (wm2000->anc_download == NULL) {
dev_err(&i2c->dev, "Out of memory\n");
ret = -ENOMEM;
- goto out_regmap_exit;
+ goto err_supplies;
}
wm2000->anc_download[0] = 0x80;
wm2000_reset(wm2000);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0);
- if (!ret)
- goto out;
-out_regmap_exit:
- regmap_exit(wm2000->regmap);
+err_supplies:
+ regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);
+
out:
release_firmware(fw);
return ret;
static __devexit int wm2000_i2c_remove(struct i2c_client *i2c)
{
- struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
-
snd_soc_unregister_codec(&i2c->dev);
- regmap_exit(wm2000->regmap);
return 0;
}
.id_table = wm2000_i2c_id,
};
-static int __init wm2000_init(void)
-{
- return i2c_add_driver(&wm2000_i2c_driver);
-}
-module_init(wm2000_init);
-
-static void __exit wm2000_exit(void)
-{
- i2c_del_driver(&wm2000_i2c_driver);
-}
-module_exit(wm2000_exit);
+module_i2c_driver(wm2000_i2c_driver);
MODULE_DESCRIPTION("ASoC WM2000 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", WM2200_MIC_BIAS_CTRL_2, WM2200_MICB2_ENA_SHIFT,
0, NULL, 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
-SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 20),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 20, 0),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
.id_table = wm2200_i2c_id,
};
-static int __init wm2200_modinit(void)
-{
- return i2c_add_driver(&wm2200_i2c_driver);
-}
-module_init(wm2200_modinit);
-
-static void __exit wm2200_exit(void)
-{
- i2c_del_driver(&wm2200_i2c_driver);
-}
-module_exit(wm2200_exit);
+module_i2c_driver(wm2200_i2c_driver);
MODULE_DESCRIPTION("ASoC WM2200 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
SND_SOC_DAPM_SUPPLY("ASYNCCLK", WM5100_CLOCKING_6, WM5100_ASYNC_CLK_ENA_SHIFT,
0, NULL, 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
SND_SOC_DAPM_SUPPLY("CP1", WM5100_HP_CHARGE_PUMP_1, WM5100_CP1_ENA_SHIFT, 0,
NULL, 0),
ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+static const char *wm5102_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm5102_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const struct soc_enum wm5102_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
+ ARIZONA_AEC_LOOPBACK_SRC_MASK,
+ ARRAY_SIZE(wm5102_aec_loopback_texts),
+ wm5102_aec_loopback_texts,
+ wm5102_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
+ SOC_DAPM_VALUE_ENUM("AEC Loopback", wm5102_aec_loopback);
+
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
-
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
-SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
+
SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
{ name, "IN2L", "IN2L PGA" }, \
ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+ { "AEC Loopback", "HPOUT1L", "OUT1L" },
+ { "AEC Loopback", "HPOUT1R", "OUT1R" },
{ "HPOUT1L", NULL, "OUT1L" },
{ "HPOUT1R", NULL, "OUT1R" },
+ { "AEC Loopback", "HPOUT2L", "OUT2L" },
+ { "AEC Loopback", "HPOUT2R", "OUT2R" },
{ "HPOUT2L", NULL, "OUT2L" },
{ "HPOUT2R", NULL, "OUT2R" },
+ { "AEC Loopback", "EPOUT", "OUT3L" },
{ "EPOUTN", NULL, "OUT3L" },
{ "EPOUTP", NULL, "OUT3L" },
+ { "AEC Loopback", "SPKOUTL", "OUT4L" },
{ "SPKOUTLN", NULL, "OUT4L" },
{ "SPKOUTLP", NULL, "OUT4L" },
+ { "AEC Loopback", "SPKOUTR", "OUT4R" },
{ "SPKOUTRN", NULL, "OUT4R" },
{ "SPKOUTRP", NULL, "OUT4R" },
+ { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+ { "AEC Loopback", "SPKDAT1R", "OUT5R" },
{ "SPKDAT1L", NULL, "OUT5L" },
{ "SPKDAT1R", NULL, "OUT5R" },
};
SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP4L", ARIZONA_DSP4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP5R", ARIZONA_DSP4RMIX_INPUT_1_SOURCE),
+
ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
-ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT3L", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT3R", ARIZONA_OUT3RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
ARIZONA_OUT1_OSR_SHIFT, 1, 0),
SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
ARIZONA_OUT2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+SOC_SINGLE("OUT3 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
ARIZONA_OUT3_OSR_SHIFT, 1, 0),
SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
ARIZONA_OUT4_OSR_SHIFT, 1, 0),
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
- ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("OUT3 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
0xbf, 0, digital_tlv),
-SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
- ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("OUT3 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+ ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_VOL_SHIFT,
+ 0xbf, 0, digital_tlv),
SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
0xbf, 0, digital_tlv),
ARIZONA_OUTPUT_PATH_CONFIG_2R,
ARIZONA_OUT2L_PGA_VOL_SHIFT,
0x34, 0x40, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("OUT3 Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUTPUT_PATH_CONFIG_3R,
+ ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3L, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3R, ARIZONA_OUT3RMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+static const char *wm5110_aec_loopback_texts[] = {
+ "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
+ "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R",
+};
+
+static const unsigned int wm5110_aec_loopback_values[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+};
+
+static const struct soc_enum wm5110_aec_loopback =
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
+ ARIZONA_AEC_LOOPBACK_SRC_MASK,
+ ARRAY_SIZE(wm5110_aec_loopback_texts),
+ wm5110_aec_loopback_texts,
+ wm5110_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
+ SOC_DAPM_VALUE_ENUM("AEC Loopback", wm5110_aec_loopback);
+
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
-
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
-SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0),
-SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+ ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+ ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
+SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+ ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5110_aec_loopback_mux),
+
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3R", ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT3R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
-ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(OUT3L, "HPOUT3L"),
+ARIZONA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),
ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
SND_SOC_DAPM_OUTPUT("HPOUT2L"),
SND_SOC_DAPM_OUTPUT("HPOUT2R"),
-SND_SOC_DAPM_OUTPUT("EPOUTN"),
-SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("HPOUT3L"),
+SND_SOC_DAPM_OUTPUT("HPOUT3R"),
SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
{ name, "IN2L", "IN2L PGA" }, \
{ "OUT2L", NULL, "CPVDD" },
{ "OUT2R", NULL, "CPVDD" },
{ "OUT3L", NULL, "CPVDD" },
+ { "OUT3R", NULL, "CPVDD" },
{ "OUT4L", NULL, "SPKVDDL" },
{ "OUT4R", NULL, "SPKVDDR" },
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
- ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+ ARIZONA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
+ ARIZONA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
{ "HPOUT2L", NULL, "OUT2L" },
{ "HPOUT2R", NULL, "OUT2R" },
- { "EPOUTN", NULL, "OUT3L" },
- { "EPOUTP", NULL, "OUT3L" },
+ { "HPOUT3L", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3L" },
{ "SPKOUTLN", NULL, "OUT4L" },
{ "SPKOUTLP", NULL, "OUT4L" },
ARIZONA_ADC_DIGITAL_VOLUME_2R,
ARIZONA_ADC_DIGITAL_VOLUME_3L,
ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_ADC_DIGITAL_VOLUME_4L,
+ ARIZONA_ADC_DIGITAL_VOLUME_4R,
ARIZONA_DAC_DIGITAL_VOLUME_1L,
ARIZONA_DAC_DIGITAL_VOLUME_1R,
ARIZONA_DAC_DIGITAL_VOLUME_4R,
ARIZONA_DAC_DIGITAL_VOLUME_5L,
ARIZONA_DAC_DIGITAL_VOLUME_5R,
+ ARIZONA_DAC_DIGITAL_VOLUME_6L,
+ ARIZONA_DAC_DIGITAL_VOLUME_6R,
};
static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
* We can't read the WM8510 register space when we are
* using 2 wire for device control, so we cache them instead.
*/
-static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0050, 0x0000, 0x0140, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x00ff,
- 0x0000, 0x0000, 0x0100, 0x00ff,
- 0x0000, 0x0000, 0x012c, 0x002c,
- 0x002c, 0x002c, 0x002c, 0x0000,
- 0x0032, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0038, 0x000b, 0x0032, 0x0000,
- 0x0008, 0x000c, 0x0093, 0x00e9,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0003, 0x0010, 0x0000, 0x0000,
- 0x0000, 0x0002, 0x0001, 0x0000,
- 0x0000, 0x0000, 0x0039, 0x0000,
- 0x0001,
+static const struct reg_default wm8510_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0x0000 },
+ { 3, 0x0000 },
+ { 4, 0x0050 },
+ { 5, 0x0000 },
+ { 6, 0x0140 },
+ { 7, 0x0000 },
+ { 8, 0x0000 },
+ { 9, 0x0000 },
+ { 10, 0x0000 },
+ { 11, 0x00ff },
+ { 12, 0x0000 },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00ff },
+ { 16, 0x0000 },
+ { 17, 0x0000 },
+ { 18, 0x012c },
+ { 19, 0x002c },
+ { 20, 0x002c },
+ { 21, 0x002c },
+ { 22, 0x002c },
+ { 23, 0x0000 },
+ { 24, 0x0032 },
+ { 25, 0x0000 },
+ { 26, 0x0000 },
+ { 27, 0x0000 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0038 },
+ { 33, 0x000b },
+ { 34, 0x0032 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x000c },
+ { 38, 0x0093 },
+ { 39, 0x00e9 },
+ { 40, 0x0000 },
+ { 41, 0x0000 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0003 },
+ { 45, 0x0010 },
+ { 46, 0x0000 },
+ { 47, 0x0000 },
+ { 48, 0x0000 },
+ { 49, 0x0002 },
+ { 50, 0x0001 },
+ { 51, 0x0000 },
+ { 52, 0x0000 },
+ { 53, 0x0000 },
+ { 54, 0x0039 },
+ { 55, 0x0000 },
+ { 56, 0x0001 },
};
+static bool wm8510_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8510_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
#define WM8510_POWER1_BIASEN 0x08
#define WM8510_POWER1_BUFIOEN 0x10
/* codec private data */
struct wm8510_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
};
static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
static int wm8510_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8510_priv *wm8510 = snd_soc_codec_get_drvdata(codec);
u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3;
switch (level) {
power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8510->regmap);
/* Initial cap charge at VMID 5k */
snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
static int wm8510_probe(struct snd_soc_codec *codec)
{
- struct wm8510_priv *wm8510 = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8510->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n", ret);
return ret;
.suspend = wm8510_suspend,
.resume = wm8510_resume,
.set_bias_level = wm8510_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8510_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default =wm8510_reg,
.controls = wm8510_snd_controls,
.num_controls = ARRAY_SIZE(wm8510_snd_controls),
{ },
};
+static const struct regmap_config wm8510_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8510_MONOMIX,
+
+ .reg_defaults = wm8510_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8510_volatile,
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8510_spi_probe(struct spi_device *spi)
{
struct wm8510_priv *wm8510;
int ret;
- wm8510 = kzalloc(sizeof(struct wm8510_priv), GFP_KERNEL);
+ wm8510 = devm_kzalloc(&spi->dev, sizeof(struct wm8510_priv),
+ GFP_KERNEL);
if (wm8510 == NULL)
return -ENOMEM;
- wm8510->control_type = SND_SOC_SPI;
+ wm8510->regmap = devm_regmap_init_spi(spi, &wm8510_regmap);
+ if (IS_ERR(wm8510->regmap))
+ return PTR_ERR(wm8510->regmap);
+
spi_set_drvdata(spi, wm8510);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8510, &wm8510_dai, 1);
- if (ret < 0)
- kfree(wm8510);
+
return ret;
}
struct wm8510_priv *wm8510;
int ret;
- wm8510 = kzalloc(sizeof(struct wm8510_priv), GFP_KERNEL);
+ wm8510 = devm_kzalloc(&i2c->dev, sizeof(struct wm8510_priv),
+ GFP_KERNEL);
if (wm8510 == NULL)
return -ENOMEM;
+ wm8510->regmap = devm_regmap_init_i2c(i2c, &wm8510_regmap);
+ if (IS_ERR(wm8510->regmap))
+ return PTR_ERR(wm8510->regmap);
+
i2c_set_clientdata(i2c, wm8510);
- wm8510->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8510, &wm8510_dai, 1);
- if (ret < 0)
- kfree(wm8510);
+
return ret;
}
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/of_device.h>
/* codec private data */
struct wm8523_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES];
unsigned int sysclk;
unsigned int rate_constraint_list[WM8523_NUM_RATES];
struct snd_pcm_hw_constraint_list rate_constraint;
};
-static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
- 0x8523, /* R0 - DEVICE_ID */
- 0x0001, /* R1 - REVISION */
- 0x0000, /* R2 - PSCTRL1 */
- 0x1812, /* R3 - AIF_CTRL1 */
- 0x0000, /* R4 - AIF_CTRL2 */
- 0x0001, /* R5 - DAC_CTRL3 */
- 0x0190, /* R6 - DAC_GAINL */
- 0x0190, /* R7 - DAC_GAINR */
- 0x0000, /* R8 - ZERO_DETECT */
+static const struct reg_default wm8523_reg_defaults[] = {
+ { 2, 0x0000 }, /* R2 - PSCTRL1 */
+ { 3, 0x1812 }, /* R3 - AIF_CTRL1 */
+ { 4, 0x0000 }, /* R4 - AIF_CTRL2 */
+ { 5, 0x0001 }, /* R5 - DAC_CTRL3 */
+ { 6, 0x0190 }, /* R6 - DAC_GAINL */
+ { 7, 0x0190 }, /* R7 - DAC_GAINR */
+ { 8, 0x0000 }, /* R8 - ZERO_DETECT */
};
-static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
+static bool wm8523_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8523_DEVICE_ID:
case WM8523_REVISION:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
-static int wm8523_reset(struct snd_soc_codec *codec)
-{
- return snd_soc_write(codec, WM8523_DEVICE_ID, 0);
-}
-
static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0);
static const char *wm8523_zd_count_text[] = {
enum snd_soc_bias_level level)
{
struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
- u16 *reg_cache = codec->reg_cache;
- int ret, i;
+ int ret;
switch (level) {
case SND_SOC_BIAS_ON:
return ret;
}
+ /* Sync back default/cached values */
+ regcache_sync(wm8523->regmap);
+
/* Initial power up */
snd_soc_update_bits(codec, WM8523_PSCTRL1,
WM8523_SYS_ENA_MASK, 1);
- /* Sync back default/cached values */
- for (i = WM8523_AIF_CTRL1;
- i < WM8523_MAX_REGISTER; i++)
- snd_soc_write(codec, i, reg_cache[i]);
-
-
msleep(100);
}
static int wm8523_probe(struct snd_soc_codec *codec)
{
struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
- int ret, i;
+ int ret;
wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
wm8523->rate_constraint.count =
ARRAY_SIZE(wm8523->rate_constraint_list);
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8523->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
- for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
- wm8523->supplies[i].supply = wm8523_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies),
- wm8523->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
- wm8523->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
- goto err_get;
- }
-
- ret = snd_soc_read(codec, WM8523_DEVICE_ID);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to read ID register\n");
- goto err_enable;
- }
- if (ret != wm8523_reg[WM8523_DEVICE_ID]) {
- dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret);
- ret = -EINVAL;
- goto err_enable;
- }
-
- ret = snd_soc_read(codec, WM8523_REVISION);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to read revision register\n");
- goto err_enable;
- }
- dev_info(codec->dev, "revision %c\n",
- (ret & WM8523_CHIP_REV_MASK) + 'A');
-
- ret = wm8523_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- goto err_enable;
- }
-
/* Change some default settings - latch VU and enable ZC */
snd_soc_update_bits(codec, WM8523_DAC_GAINR,
WM8523_DACR_VU, WM8523_DACR_VU);
wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- /* Bias level configuration will have done an extra enable */
- regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
-
return 0;
-
-err_enable:
- regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
-err_get:
- regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
-
- return ret;
}
static int wm8523_remove(struct snd_soc_codec *codec)
{
- struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
-
wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
- regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
return 0;
}
.suspend = wm8523_suspend,
.resume = wm8523_resume,
.set_bias_level = wm8523_set_bias_level,
- .reg_cache_size = WM8523_REGISTER_COUNT,
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8523_reg,
- .volatile_register = wm8523_volatile_register,
.controls = wm8523_controls,
.num_controls = ARRAY_SIZE(wm8523_controls),
{ },
};
+static const struct regmap_config wm8523_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8523_ZERO_DETECT,
+
+ .reg_defaults = wm8523_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8523_volatile_register,
+};
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8523_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8523_priv *wm8523;
- int ret;
+ unsigned int val;
+ int ret, i;
- wm8523 = kzalloc(sizeof(struct wm8523_priv), GFP_KERNEL);
+ wm8523 = devm_kzalloc(&i2c->dev, sizeof(struct wm8523_priv),
+ GFP_KERNEL);
if (wm8523 == NULL)
return -ENOMEM;
+ wm8523->regmap = devm_regmap_init_i2c(i2c, &wm8523_regmap);
+ if (IS_ERR(wm8523->regmap)) {
+ ret = PTR_ERR(wm8523->regmap);
+ dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
+ wm8523->supplies[i].supply = wm8523_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(wm8523->regmap, WM8523_DEVICE_ID, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read ID register\n");
+ goto err_enable;
+ }
+ if (val != 0x8523) {
+ dev_err(&i2c->dev, "Device is not a WM8523, ID is %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = regmap_read(wm8523->regmap, WM8523_REVISION, &val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read revision register\n");
+ goto err_enable;
+ }
+ dev_info(&i2c->dev, "revision %c\n",
+ (val & WM8523_CHIP_REV_MASK) + 'A');
+
+ ret = regmap_write(wm8523->regmap, WM8523_DEVICE_ID, 0x8523);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to reset device: %d\n", ret);
+ goto err_enable;
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+
i2c_set_clientdata(i2c, wm8523);
- wm8523->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8523, &wm8523_dai, 1);
- if (ret < 0)
- kfree(wm8523);
+
return ret;
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+ return ret;
}
static __devexit int wm8523_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
/*
* wm8580.c -- WM8580 ALSA Soc Audio driver
*
- * Copyright 2008-11 Wolfson Microelectronics PLC.
+ * Copyright 2008-12 Wolfson Microelectronics PLC.
*
* 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
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/of_device.h>
* We can't read the WM8580 register space when we
* are using 2 wire for device control, so we cache them instead.
*/
-static const u16 wm8580_reg[] = {
- 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/
- 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/
- 0x0010, 0x0002, 0x0002, 0x00c2, /*R11*/
- 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/
- 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/
- 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/
- 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/
- 0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/
- 0x0000, 0x0000, 0x0031, 0x000b, /*R35*/
- 0x0039, 0x0000, 0x0010, 0x0032, /*R39*/
- 0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/
- 0x0000, 0x0000, 0x0000, 0x0000, /*R47*/
- 0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/
- 0x0000, 0x0000 /*R53*/
+static const struct reg_default wm8580_reg_defaults[] = {
+ { 0, 0x0121 },
+ { 1, 0x017e },
+ { 2, 0x007d },
+ { 3, 0x0014 },
+ { 4, 0x0121 },
+ { 5, 0x017e },
+ { 6, 0x007d },
+ { 7, 0x0194 },
+ { 8, 0x0010 },
+ { 9, 0x0002 },
+ { 10, 0x0002 },
+ { 11, 0x00c2 },
+ { 12, 0x0182 },
+ { 13, 0x0082 },
+ { 14, 0x000a },
+ { 15, 0x0024 },
+ { 16, 0x0009 },
+ { 17, 0x0000 },
+ { 18, 0x00ff },
+ { 19, 0x0000 },
+ { 20, 0x00ff },
+ { 21, 0x00ff },
+ { 22, 0x00ff },
+ { 23, 0x00ff },
+ { 24, 0x00ff },
+ { 25, 0x00ff },
+ { 26, 0x00ff },
+ { 27, 0x00ff },
+ { 28, 0x01f0 },
+ { 29, 0x0040 },
+ { 30, 0x0000 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0031 },
+ { 35, 0x000b },
+ { 36, 0x0039 },
+ { 37, 0x0000 },
+ { 38, 0x0010 },
+ { 39, 0x0032 },
+ { 40, 0x0054 },
+ { 41, 0x0076 },
+ { 42, 0x0098 },
+ { 43, 0x0000 },
+ { 44, 0x0000 },
+ { 45, 0x0000 },
+ { 46, 0x0000 },
+ { 47, 0x0000 },
+ { 48, 0x0000 },
+ { 49, 0x0000 },
+ { 50, 0x005e },
+ { 51, 0x003e },
+ { 52, 0x0000 },
};
+static bool wm8580_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8580_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct pll_state {
unsigned int in;
unsigned int out;
/* codec private data */
struct wm8580_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
struct pll_state a;
struct pll_state b;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- u16 *reg_cache = codec->reg_cache;
+ struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int ret;
- /* Clear the register cache so we write without VU set */
- reg_cache[reg] = 0;
- reg_cache[reg2] = 0;
+ /* Clear the register cache VU so we write without VU set */
+ regcache_cache_only(wm8580->regmap, true);
+ regmap_update_bits(wm8580->regmap, reg, 0x100, 0x000);
+ regmap_update_bits(wm8580->regmap, reg2, 0x100, 0x000);
+ regcache_cache_only(wm8580->regmap, false);
ret = snd_soc_put_volsw(kcontrol, ucontrol);
if (ret < 0)
static int wm8580_probe(struct snd_soc_codec *codec)
{
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
- int ret = 0,i;
+ int ret = 0;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8580->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
- for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
- wm8580->supplies[i].supply = wm8580_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
- wm8580->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
wm8580->supplies);
if (ret != 0) {
err_regulator_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
err_regulator_get:
- regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
return ret;
}
wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
- regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
return 0;
}
.probe = wm8580_probe,
.remove = wm8580_remove,
.set_bias_level = wm8580_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8580_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8580_reg,
.controls = wm8580_snd_controls,
.num_controls = ARRAY_SIZE(wm8580_snd_controls),
{ },
};
+static const struct regmap_config wm8580_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8580_MAX_REGISTER,
+
+ .reg_defaults = wm8580_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8580_volatile,
+};
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8580_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8580_priv *wm8580;
- int ret;
+ int ret, i;
- wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
+ wm8580 = devm_kzalloc(&i2c->dev, sizeof(struct wm8580_priv),
+ GFP_KERNEL);
if (wm8580 == NULL)
return -ENOMEM;
+ wm8580->regmap = devm_regmap_init_i2c(i2c, &wm8580_regmap);
+ if (IS_ERR(wm8580->regmap))
+ return PTR_ERR(wm8580->regmap);
+
+ for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+ wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8580->supplies),
+ wm8580->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8580);
- wm8580->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai));
- if (ret < 0)
- kfree(wm8580);
+
return ret;
}
static int wm8580_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/of_device.h>
/* codec private data */
struct wm8711_priv {
- enum snd_soc_control_type bus_type;
+ struct regmap *regmap;
unsigned int sysclk;
};
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
-static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
- 0x0079, 0x0079, 0x000a, 0x0008,
- 0x009f, 0x000a, 0x0000, 0x0000
+static const struct reg_default wm8711_reg_defaults[] = {
+ { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 },
+ { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 },
};
+static bool wm8711_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8711_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static int wm8711_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
switch (level) {
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8711->regmap);
snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
break;
static int wm8711_probe(struct snd_soc_codec *codec)
{
- struct wm8711_priv *wm8711 = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8711->bus_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
.suspend = wm8711_suspend,
.resume = wm8711_resume,
.set_bias_level = wm8711_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8711_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8711_reg,
.controls = wm8711_snd_controls,
.num_controls = ARRAY_SIZE(wm8711_snd_controls),
.dapm_widgets = wm8711_dapm_widgets,
};
MODULE_DEVICE_TABLE(of, wm8711_of_match);
+static const struct regmap_config wm8711_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8711_RESET,
+
+ .reg_defaults = wm8711_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8711_volatile,
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8711_spi_probe(struct spi_device *spi)
{
struct wm8711_priv *wm8711;
int ret;
- wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv),
+ GFP_KERNEL);
if (wm8711 == NULL)
return -ENOMEM;
+ wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap);
+ if (IS_ERR(wm8711->regmap))
+ return PTR_ERR(wm8711->regmap);
+
spi_set_drvdata(spi, wm8711);
- wm8711->bus_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8711, &wm8711_dai, 1);
- if (ret < 0)
- kfree(wm8711);
+
return ret;
}
static int __devexit wm8711_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
+
return 0;
}
struct wm8711_priv *wm8711;
int ret;
- wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv),
+ GFP_KERNEL);
if (wm8711 == NULL)
return -ENOMEM;
+ wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap);
+ if (IS_ERR(wm8711->regmap))
+ return PTR_ERR(wm8711->regmap);
+
i2c_set_clientdata(client, wm8711);
- wm8711->bus_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_wm8711, &wm8711_dai, 1);
- if (ret < 0)
- kfree(wm8711);
+
return ret;
}
static __devexit int wm8711_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/of_device.h>
* the volume update bits, mute the output and enable infinite zero
* detect.
*/
-static const u16 wm8728_reg_defaults[] = {
- 0x1ff,
- 0x1ff,
- 0x001,
- 0x100,
+static const struct reg_default wm8728_reg_defaults[] = {
+ { 0, 0x1ff },
+ { 1, 0x1ff },
+ { 2, 0x001 },
+ { 3, 0x100 },
};
/* codec private data */
struct wm8728_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
};
static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
static int wm8728_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec);
u16 reg;
- int i;
switch (level) {
case SND_SOC_BIAS_ON:
snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
/* ..then sync in the register cache. */
- for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
- snd_soc_write(codec, i,
- snd_soc_read(codec, i));
+ regcache_sync(wm8728->regmap);
}
break;
static int wm8728_probe(struct snd_soc_codec *codec)
{
- struct wm8728_priv *wm8728 = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8728->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n",
ret);
.suspend = wm8728_suspend,
.resume = wm8728_resume,
.set_bias_level = wm8728_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8728_reg_defaults,
.controls = wm8728_snd_controls,
.num_controls = ARRAY_SIZE(wm8728_snd_controls),
.dapm_widgets = wm8728_dapm_widgets,
};
MODULE_DEVICE_TABLE(of, wm8728_of_match);
+static const struct regmap_config wm8728_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8728_IFCTL,
+
+ .reg_defaults = wm8728_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8728_spi_probe(struct spi_device *spi)
{
struct wm8728_priv *wm8728;
int ret;
- wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL);
+ wm8728 = devm_kzalloc(&spi->dev, sizeof(struct wm8728_priv),
+ GFP_KERNEL);
if (wm8728 == NULL)
return -ENOMEM;
- wm8728->control_type = SND_SOC_SPI;
+ wm8728->regmap = devm_regmap_init_spi(spi, &wm8728_regmap);
+ if (IS_ERR(wm8728->regmap))
+ return PTR_ERR(wm8728->regmap);
+
spi_set_drvdata(spi, wm8728);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8728, &wm8728_dai, 1);
- if (ret < 0)
- kfree(wm8728);
+
return ret;
}
static int __devexit wm8728_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
+
return 0;
}
struct wm8728_priv *wm8728;
int ret;
- wm8728 = kzalloc(sizeof(struct wm8728_priv), GFP_KERNEL);
+ wm8728 = devm_kzalloc(&i2c->dev, sizeof(struct wm8728_priv),
+ GFP_KERNEL);
if (wm8728 == NULL)
return -ENOMEM;
+ wm8728->regmap = devm_regmap_init_i2c(i2c, &wm8728_regmap);
+ if (IS_ERR(wm8728->regmap))
+ return PTR_ERR(wm8728->regmap);
+
i2c_set_clientdata(i2c, wm8728);
- wm8728->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8728, &wm8728_dai, 1);
- if (ret < 0)
- kfree(wm8728);
+
return ret;
}
static __devexit int wm8728_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
/* codec private data */
struct wm8737_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
struct regulator_bulk_data supplies[WM8737_NUM_SUPPLIES];
unsigned int mclk;
};
-static const u16 wm8737_reg[WM8737_REGISTER_COUNT] = {
- 0x00C3, /* R0 - Left PGA volume */
- 0x00C3, /* R1 - Right PGA volume */
- 0x0007, /* R2 - AUDIO path L */
- 0x0007, /* R3 - AUDIO path R */
- 0x0000, /* R4 - 3D Enhance */
- 0x0000, /* R5 - ADC Control */
- 0x0000, /* R6 - Power Management */
- 0x000A, /* R7 - Audio Format */
- 0x0000, /* R8 - Clocking */
- 0x000F, /* R9 - MIC Preamp Control */
- 0x0003, /* R10 - Misc Bias Control */
- 0x0000, /* R11 - Noise Gate */
- 0x007C, /* R12 - ALC1 */
- 0x0000, /* R13 - ALC2 */
- 0x0032, /* R14 - ALC3 */
+static const struct reg_default wm8737_reg_defaults[] = {
+ { 0, 0x00C3 }, /* R0 - Left PGA volume */
+ { 1, 0x00C3 }, /* R1 - Right PGA volume */
+ { 2, 0x0007 }, /* R2 - AUDIO path L */
+ { 3, 0x0007 }, /* R3 - AUDIO path R */
+ { 4, 0x0000 }, /* R4 - 3D Enhance */
+ { 5, 0x0000 }, /* R5 - ADC Control */
+ { 6, 0x0000 }, /* R6 - Power Management */
+ { 7, 0x000A }, /* R7 - Audio Format */
+ { 8, 0x0000 }, /* R8 - Clocking */
+ { 9, 0x000F }, /* R9 - MIC Preamp Control */
+ { 10, 0x0003 }, /* R10 - Misc Bias Control */
+ { 11, 0x0000 }, /* R11 - Noise Gate */
+ { 12, 0x007C }, /* R12 - ALC1 */
+ { 13, 0x0000 }, /* R13 - ALC2 */
+ { 14, 0x0032 }, /* R14 - ALC3 */
};
+static bool wm8737_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8737_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int wm8737_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, WM8737_RESET, 0);
return ret;
}
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8737->regmap);
/* Fast VMID ramp at 2*2.5k */
snd_soc_update_bits(codec, WM8737_MISC_BIAS_CONTROL,
static int wm8737_probe(struct snd_soc_codec *codec)
{
struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
- int ret, i;
+ int ret;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8737->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
- for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++)
- wm8737->supplies[i].supply = wm8737_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8737->supplies),
- wm8737->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies),
wm8737->supplies);
if (ret != 0) {
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
err_get:
- regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
-
return ret;
}
static int wm8737_remove(struct snd_soc_codec *codec)
{
- struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
-
wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF);
- regulator_bulk_free(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
return 0;
}
.suspend = wm8737_suspend,
.resume = wm8737_resume,
.set_bias_level = wm8737_set_bias_level,
-
- .reg_cache_size = WM8737_REGISTER_COUNT - 1, /* Skip reset */
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8737_reg,
};
static const struct of_device_id wm8737_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8737_of_match);
+static const struct regmap_config wm8737_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8737_MAX_REGISTER,
+
+ .reg_defaults = wm8737_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8737_volatile,
+};
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8737_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8737_priv *wm8737;
- int ret;
+ int ret, i;
- wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL);
+ wm8737 = devm_kzalloc(&i2c->dev, sizeof(struct wm8737_priv),
+ GFP_KERNEL);
if (wm8737 == NULL)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++)
+ wm8737->supplies[i].supply = wm8737_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8737->regmap = devm_regmap_init_i2c(i2c, &wm8737_regmap);
+ if (IS_ERR(wm8737->regmap))
+ return PTR_ERR(wm8737->regmap);
+
i2c_set_clientdata(i2c, wm8737);
- wm8737->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8737, &wm8737_dai, 1);
- if (ret < 0)
- kfree(wm8737);
+
return ret;
}
static __devexit int wm8737_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
+
return 0;
}
static int __devinit wm8737_spi_probe(struct spi_device *spi)
{
struct wm8737_priv *wm8737;
- int ret;
+ int ret, i;
- wm8737 = kzalloc(sizeof(struct wm8737_priv), GFP_KERNEL);
+ wm8737 = devm_kzalloc(&spi->dev, sizeof(struct wm8737_priv),
+ GFP_KERNEL);
if (wm8737 == NULL)
return -ENOMEM;
- wm8737->control_type = SND_SOC_SPI;
+ for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++)
+ wm8737->supplies[i].supply = wm8737_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8737->supplies),
+ wm8737->supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8737->regmap = devm_regmap_init_spi(spi, &wm8737_regmap);
+ if (IS_ERR(wm8737->regmap))
+ return PTR_ERR(wm8737->regmap);
+
spi_set_drvdata(spi, wm8737);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8737, &wm8737_dai, 1);
- if (ret < 0)
- kfree(wm8737);
+
return ret;
}
static int __devexit wm8737_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
+
return 0;
}
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/of_device.h>
/* codec private data */
struct wm8741_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
unsigned int sysclk;
struct snd_pcm_hw_constraint_list *sysclk_constraints;
};
-static const u16 wm8741_reg_defaults[WM8741_REGISTER_COUNT] = {
- 0x0000, /* R0 - DACLLSB Attenuation */
- 0x0000, /* R1 - DACLMSB Attenuation */
- 0x0000, /* R2 - DACRLSB Attenuation */
- 0x0000, /* R3 - DACRMSB Attenuation */
- 0x0000, /* R4 - Volume Control */
- 0x000A, /* R5 - Format Control */
- 0x0000, /* R6 - Filter Control */
- 0x0000, /* R7 - Mode Control 1 */
- 0x0002, /* R8 - Mode Control 2 */
- 0x0000, /* R9 - Reset */
- 0x0002, /* R32 - ADDITONAL_CONTROL_1 */
+static const struct reg_default wm8741_reg_defaults[] = {
+ { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */
+ { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */
+ { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */
+ { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */
+ { 4, 0x0000 }, /* R4 - Volume Control */
+ { 5, 0x000A }, /* R5 - Format Control */
+ { 6, 0x0000 }, /* R6 - Filter Control */
+ { 7, 0x0000 }, /* R7 - Mode Control 1 */
+ { 8, 0x0002 }, /* R8 - Mode Control 2 */
+ { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */
};
+static bool wm8741_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8741_DACLLSB_ATTENUATION:
+ case WM8741_DACLMSB_ATTENUATION:
+ case WM8741_DACRLSB_ATTENUATION:
+ case WM8741_DACRMSB_ATTENUATION:
+ case WM8741_VOLUME_CONTROL:
+ case WM8741_FORMAT_CONTROL:
+ case WM8741_FILTER_CONTROL:
+ case WM8741_MODE_CONTROL_1:
+ case WM8741_MODE_CONTROL_2:
+ case WM8741_ADDITIONAL_CONTROL_1:
+ return true;
+ default:
+ return false;
+ }
+}
static int wm8741_reset(struct snd_soc_codec *codec)
{
{
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
- wm8741->supplies[i].supply = wm8741_supply_names[i];
-
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies),
- wm8741->supplies);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- goto err;
- }
ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
wm8741->supplies);
goto err_get;
}
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
goto err_enable;
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
err_get:
- regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
-err:
return ret;
}
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
- regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
return 0;
}
.probe = wm8741_probe,
.remove = wm8741_remove,
.resume = wm8741_resume,
- .reg_cache_size = ARRAY_SIZE(wm8741_reg_defaults),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8741_reg_defaults,
.controls = wm8741_snd_controls,
.num_controls = ARRAY_SIZE(wm8741_snd_controls),
};
MODULE_DEVICE_TABLE(of, wm8741_of_match);
+static const struct regmap_config wm8741_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8741_MAX_REGISTER,
+
+ .reg_defaults = wm8741_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = wm8741_readable,
+};
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8741_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8741_priv *wm8741;
- int ret;
+ int ret, i;
wm8741 = devm_kzalloc(&i2c->dev, sizeof(struct wm8741_priv),
GFP_KERNEL);
if (wm8741 == NULL)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+ wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies),
+ wm8741->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8741->regmap = regmap_init_i2c(i2c, &wm8741_regmap);
+ if (IS_ERR(wm8741->regmap)) {
+ ret = PTR_ERR(wm8741->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8741);
- wm8741->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8741, &wm8741_dai, 1);
static int __devinit wm8741_spi_probe(struct spi_device *spi)
{
struct wm8741_priv *wm8741;
- int ret;
+ int ret, i;
wm8741 = devm_kzalloc(&spi->dev, sizeof(struct wm8741_priv),
GFP_KERNEL);
if (wm8741 == NULL)
return -ENOMEM;
- wm8741->control_type = SND_SOC_SPI;
+ for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+ wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8741->supplies),
+ wm8741->supplies);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ wm8741->regmap = regmap_init_spi(spi, &wm8741_regmap);
+ if (IS_ERR(wm8741->regmap)) {
+ ret = PTR_ERR(wm8741->regmap);
+ dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
spi_set_drvdata(spi, wm8741);
ret = snd_soc_register_codec(&spi->dev,
.remove = __devexit_p(wm8770_spi_remove)
};
-static int __init wm8770_modinit(void)
-{
- int ret = 0;
-
- ret = spi_register_driver(&wm8770_spi_driver);
- if (ret) {
- printk(KERN_ERR "Failed to register wm8770 SPI driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8770_modinit);
-
-static void __exit wm8770_exit(void)
-{
- spi_unregister_driver(&wm8770_spi_driver);
-}
-module_exit(wm8770_exit);
+module_spi_driver(wm8770_spi_driver);
MODULE_DESCRIPTION("ASoC WM8770 driver");
MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
/* codec private data */
struct wm8776_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
int sysclk[2];
};
-static const u16 wm8776_reg[WM8776_CACHEREGNUM] = {
- 0x79, 0x79, 0x79, 0xff, 0xff, /* 4 */
- 0xff, 0x00, 0x90, 0x00, 0x00, /* 9 */
- 0x22, 0x22, 0x22, 0x08, 0xcf, /* 14 */
- 0xcf, 0x7b, 0x00, 0x32, 0x00, /* 19 */
- 0xa6, 0x01, 0x01
+static const struct reg_default wm8776_reg_defaults[] = {
+ { 0, 0x79 },
+ { 1, 0x79 },
+ { 2, 0x79 },
+ { 3, 0xff },
+ { 4, 0xff },
+ { 5, 0xff },
+ { 6, 0x00 },
+ { 7, 0x90 },
+ { 8, 0x00 },
+ { 9, 0x00 },
+ { 10, 0x22 },
+ { 11, 0x22 },
+ { 12, 0x22 },
+ { 13, 0x08 },
+ { 14, 0xcf },
+ { 15, 0xcf },
+ { 16, 0x7b },
+ { 17, 0x00 },
+ { 18, 0x32 },
+ { 19, 0x00 },
+ { 20, 0xa6 },
+ { 21, 0x01 },
+ { 22, 0x01 },
};
+static bool wm8776_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8776_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int wm8776_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, WM8776_RESET, 0);
static int wm8776_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
+
switch (level) {
case SND_SOC_BIAS_ON:
break;
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8776->regmap);
/* Disable the global powerdown; DAPM does the rest */
snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0);
static int wm8776_probe(struct snd_soc_codec *codec)
{
- struct wm8776_priv *wm8776 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8776->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
.suspend = wm8776_suspend,
.resume = wm8776_resume,
.set_bias_level = wm8776_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8776_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8776_reg,
.controls = wm8776_snd_controls,
.num_controls = ARRAY_SIZE(wm8776_snd_controls),
};
MODULE_DEVICE_TABLE(of, wm8776_of_match);
+static const struct regmap_config wm8776_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8776_RESET,
+
+ .reg_defaults = wm8776_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8776_volatile,
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8776_spi_probe(struct spi_device *spi)
{
if (wm8776 == NULL)
return -ENOMEM;
- wm8776->control_type = SND_SOC_SPI;
+ wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
spi_set_drvdata(spi, wm8776);
ret = snd_soc_register_codec(&spi->dev,
if (wm8776 == NULL)
return -ENOMEM;
+ wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap);
+ if (IS_ERR(wm8776->regmap))
+ return PTR_ERR(wm8776->regmap);
+
i2c_set_clientdata(i2c, wm8776);
- wm8776->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#define WM8900_LRC_MASK 0x03ff
struct wm8900_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
u32 fll_in; /* FLL input frequency */
u32 fll_out; /* FLL output frequency */
* wm8900 register cache. We can't read the entire register space and we
* have slow control buses so we cache the registers.
*/
-static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
- 0x8900, 0x0000,
- 0xc000, 0x0000,
- 0x4050, 0x4000,
- 0x0008, 0x0000,
- 0x0040, 0x0040,
- 0x1004, 0x00c0,
- 0x00c0, 0x0000,
- 0x0100, 0x00c0,
- 0x00c0, 0x0000,
- 0xb001, 0x0000,
- 0x0000, 0x0044,
- 0x004c, 0x004c,
- 0x0044, 0x0044,
- 0x0000, 0x0044,
- 0x0000, 0x0000,
- 0x0002, 0x0000,
- 0x0000, 0x0000,
- 0x0000, 0x0000,
- 0x0008, 0x0000,
- 0x0000, 0x0008,
- 0x0097, 0x0100,
- 0x0000, 0x0000,
- 0x0050, 0x0050,
- 0x0055, 0x0055,
- 0x0055, 0x0000,
- 0x0000, 0x0079,
- 0x0079, 0x0079,
- 0x0079, 0x0000,
- /* Remaining registers all zero */
+static const struct reg_default wm8900_reg_defaults[] = {
+ { 1, 0x0000 },
+ { 2, 0xc000 },
+ { 3, 0x0000 },
+ { 4, 0x4050 },
+ { 5, 0x4000 },
+ { 6, 0x0008 },
+ { 7, 0x0000 },
+ { 8, 0x0040 },
+ { 9, 0x0040 },
+ { 10, 0x1004 },
+ { 11, 0x00c0 },
+ { 12, 0x00c0 },
+ { 13, 0x0000 },
+ { 14, 0x0100 },
+ { 15, 0x00c0 },
+ { 16, 0x00c0 },
+ { 17, 0x0000 },
+ { 18, 0xb001 },
+ { 19, 0x0000 },
+ { 20, 0x0000 },
+ { 21, 0x0044 },
+ { 22, 0x004c },
+ { 23, 0x004c },
+ { 24, 0x0044 },
+ { 25, 0x0044 },
+ { 26, 0x0000 },
+ { 27, 0x0044 },
+ { 28, 0x0000 },
+ { 29, 0x0000 },
+ { 30, 0x0002 },
+ { 31, 0x0000 },
+ { 32, 0x0000 },
+ { 33, 0x0000 },
+ { 34, 0x0000 },
+ { 35, 0x0000 },
+ { 36, 0x0008 },
+ { 37, 0x0000 },
+ { 38, 0x0000 },
+ { 39, 0x0008 },
+ { 40, 0x0097 },
+ { 41, 0x0100 },
+ { 42, 0x0000 },
+ { 43, 0x0000 },
+ { 44, 0x0050 },
+ { 45, 0x0050 },
+ { 46, 0x0055 },
+ { 47, 0x0055 },
+ { 48, 0x0055 },
+ { 49, 0x0000 },
+ { 50, 0x0000 },
+ { 51, 0x0079 },
+ { 52, 0x0079 },
+ { 53, 0x0079 },
+ { 54, 0x0079 },
+ { 55, 0x0000 },
};
-static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
+static bool wm8900_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8900_REG_ID:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
static void wm8900_reset(struct snd_soc_codec *codec)
{
snd_soc_write(codec, WM8900_REG_RESET, 0);
-
- memcpy(codec->reg_cache, wm8900_reg_defaults,
- sizeof(wm8900_reg_defaults));
}
static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0),
};
-static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" };
+static const char *wm8900_lp_mux[] = { "Disabled", "Enabled" };
static const struct soc_enum wm8900_lineout2_lp_mux =
-SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux);
+SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm8900_lp_mux);
static const struct snd_kcontrol_new wm8900_lineout2_lp =
SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux);
static int wm8900_resume(struct snd_soc_codec *codec)
{
struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
- u16 *cache;
- int i, ret;
-
- cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults),
- GFP_KERNEL);
+ int ret;
wm8900_reset(codec);
+
+ ret = regcache_sync(wm8900->regmap);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to restore cache: %d\n", ret);
+ return ret;
+ }
+
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Restart the FLL? */
ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
if (ret != 0) {
dev_err(codec->dev, "Failed to restart FLL\n");
- kfree(cache);
return ret;
}
}
- if (cache) {
- for (i = 0; i < WM8900_MAXREG; i++)
- snd_soc_write(codec, i, cache[i]);
- kfree(cache);
- } else
- dev_err(codec->dev, "Unable to allocate register cache\n");
-
return 0;
}
static int wm8900_probe(struct snd_soc_codec *codec)
{
- struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec);
int ret = 0, reg;
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8900->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
.suspend = wm8900_suspend,
.resume = wm8900_resume,
.set_bias_level = wm8900_set_bias_level,
- .volatile_register = wm8900_volatile_register,
- .reg_cache_size = ARRAY_SIZE(wm8900_reg_defaults),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8900_reg_defaults,
.controls = wm8900_snd_controls,
.num_controls = ARRAY_SIZE(wm8900_snd_controls),
.num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes),
};
+static const struct regmap_config wm8900_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8900_MAXREG,
+
+ .reg_defaults = wm8900_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8900_volatile_register,
+};
+
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8900_spi_probe(struct spi_device *spi)
{
struct wm8900_priv *wm8900;
int ret;
- wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+ wm8900 = devm_kzalloc(&spi->dev, sizeof(struct wm8900_priv),
+ GFP_KERNEL);
if (wm8900 == NULL)
return -ENOMEM;
- wm8900->control_type = SND_SOC_SPI;
+ wm8900->regmap = devm_regmap_init_spi(spi, &wm8900_regmap);
+ if (IS_ERR(wm8900->regmap))
+ return PTR_ERR(wm8900->regmap);
+
spi_set_drvdata(spi, wm8900);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8900, &wm8900_dai, 1);
- if (ret < 0)
- kfree(wm8900);
+
return ret;
}
static int __devexit wm8900_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
return 0;
}
struct wm8900_priv *wm8900;
int ret;
- wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+ wm8900 = devm_kzalloc(&i2c->dev, sizeof(struct wm8900_priv),
+ GFP_KERNEL);
if (wm8900 == NULL)
return -ENOMEM;
+ wm8900->regmap = devm_regmap_init_i2c(i2c, &wm8900_regmap);
+ if (IS_ERR(wm8900->regmap))
+ return PTR_ERR(wm8900->regmap);
+
i2c_set_clientdata(i2c, wm8900);
- wm8900->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8900, &wm8900_dai, 1);
- if (ret < 0)
- kfree(wm8900);
+
return ret;
}
static __devexit int wm8900_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
.id_table = wm8903_i2c_id,
};
-static int __init wm8903_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8903_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8903 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8903_modinit);
-
-static void __exit wm8903_exit(void)
-{
- i2c_del_driver(&wm8903_i2c_driver);
-}
-module_exit(wm8903_exit);
+module_i2c_driver(wm8903_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8903 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
snd_soc_dapm_new_controls(dapm, wm8904_dapm_widgets,
ARRAY_SIZE(wm8904_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, core_intercon,
- ARRAY_SIZE(core_intercon));
snd_soc_dapm_add_routes(dapm, adc_intercon,
ARRAY_SIZE(adc_intercon));
snd_soc_dapm_add_routes(dapm, dac_intercon,
.id_table = wm8940_i2c_id,
};
-static int __init wm8940_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8940_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8940 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8940_modinit);
-
-static void __exit wm8940_exit(void)
-{
- i2c_del_driver(&wm8940_i2c_driver);
-}
-module_exit(wm8940_exit);
+module_i2c_driver(wm8940_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8940 driver");
MODULE_AUTHOR("Jonathan Cameron");
.id_table = wm8955_i2c_id,
};
-static int __init wm8955_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8955_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8955 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8955_modinit);
-
-static void __exit wm8955_exit(void)
-{
- i2c_del_driver(&wm8955_i2c_driver);
-}
-module_exit(wm8955_exit);
+module_i2c_driver(wm8955_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8955 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
wm8994->mbc_texts = kmalloc(sizeof(char *)
* pdata->num_mbc_cfgs, GFP_KERNEL);
if (!wm8994->mbc_texts) {
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to allocate %d MBC config texts\n",
pdata->num_mbc_cfgs);
return;
wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
wm8994->mbc_enum.texts = wm8994->mbc_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
if (ret != 0)
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to add MBC mode controls: %d\n", ret);
}
wm8994->vss_texts = kmalloc(sizeof(char *)
* pdata->num_vss_cfgs, GFP_KERNEL);
if (!wm8994->vss_texts) {
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to allocate %d VSS config texts\n",
pdata->num_vss_cfgs);
return;
wm8994->vss_enum.max = pdata->num_vss_cfgs;
wm8994->vss_enum.texts = wm8994->vss_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
if (ret != 0)
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to add VSS mode controls: %d\n", ret);
}
wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
* pdata->num_vss_hpf_cfgs, GFP_KERNEL);
if (!wm8994->vss_hpf_texts) {
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to allocate %d VSS HPF config texts\n",
pdata->num_vss_hpf_cfgs);
return;
wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
if (ret != 0)
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to add VSS HPFmode controls: %d\n",
ret);
}
wm8994->enh_eq_texts = kmalloc(sizeof(char *)
* pdata->num_enh_eq_cfgs, GFP_KERNEL);
if (!wm8994->enh_eq_texts) {
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to allocate %d enhanced EQ config texts\n",
pdata->num_enh_eq_cfgs);
return;
wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, control, 1);
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ control, 1);
if (ret != 0)
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to add enhanced EQ controls: %d\n",
ret);
}
* We can't read the WM8960 register space when we are
* using 2 wire for device control, so we cache them instead.
*/
-static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0000, 0x0000,
- 0x0000, 0x0008, 0x0000, 0x000a,
- 0x01c0, 0x0000, 0x00ff, 0x00ff,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x007b, 0x0100, 0x0032,
- 0x0000, 0x00c3, 0x00c3, 0x01c0,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0100, 0x0100, 0x0050, 0x0050,
- 0x0050, 0x0050, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0040, 0x0000,
- 0x0000, 0x0050, 0x0050, 0x0000,
- 0x0002, 0x0037, 0x004d, 0x0080,
- 0x0008, 0x0031, 0x0026, 0x00e9,
+static const struct reg_default wm8960_reg_defaults[] = {
+ { 0x0, 0x0097 },
+ { 0x1, 0x0097 },
+ { 0x2, 0x0000 },
+ { 0x3, 0x0000 },
+ { 0x4, 0x0000 },
+ { 0x5, 0x0008 },
+ { 0x6, 0x0000 },
+ { 0x7, 0x000a },
+ { 0x8, 0x01c0 },
+ { 0x9, 0x0000 },
+ { 0xa, 0x00ff },
+ { 0xb, 0x00ff },
+
+ { 0x10, 0x0000 },
+ { 0x11, 0x007b },
+ { 0x12, 0x0100 },
+ { 0x13, 0x0032 },
+ { 0x14, 0x0000 },
+ { 0x15, 0x00c3 },
+ { 0x16, 0x00c3 },
+ { 0x17, 0x01c0 },
+ { 0x18, 0x0000 },
+ { 0x19, 0x0000 },
+ { 0x1a, 0x0000 },
+ { 0x1b, 0x0000 },
+ { 0x1c, 0x0000 },
+ { 0x1d, 0x0000 },
+
+ { 0x20, 0x0100 },
+ { 0x21, 0x0100 },
+ { 0x22, 0x0050 },
+
+ { 0x25, 0x0050 },
+ { 0x26, 0x0000 },
+ { 0x27, 0x0000 },
+ { 0x28, 0x0000 },
+ { 0x29, 0x0000 },
+ { 0x2a, 0x0040 },
+ { 0x2b, 0x0000 },
+ { 0x2c, 0x0000 },
+ { 0x2d, 0x0050 },
+ { 0x2e, 0x0050 },
+ { 0x2f, 0x0000 },
+ { 0x30, 0x0002 },
+ { 0x31, 0x0037 },
+
+ { 0x33, 0x0080 },
+ { 0x34, 0x0008 },
+ { 0x35, 0x0031 },
+ { 0x36, 0x0026 },
+ { 0x37, 0x00e9 },
};
+static bool wm8960_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8960_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct wm8960_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
int (*set_bias_level)(struct snd_soc_codec *,
enum snd_soc_bias_level level);
struct snd_soc_dapm_widget *lout1;
struct snd_soc_codec *codec = dai->codec;
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+ snd_pcm_format_t format = params_format(params);
int i;
/* bit size */
- switch (params_format(params)) {
+ switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
iface |= 0x0004;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_BE:
iface |= 0x0008;
break;
+ default:
+ dev_err(codec->dev, "unsupported format %i\n", format);
+ return -EINVAL;
}
/* Update filters for the new rate */
static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8960->regmap);
/* Enable anti-pop features */
snd_soc_write(codec, WM8960_APOP1,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8960->regmap);
break;
default:
break;
if (!pdata) {
dev_warn(codec->dev, "No platform data supplied\n");
} else {
- if (pdata->dres > WM8960_DRES_MAX) {
- dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
- pdata->dres = 0;
- }
-
if (pdata->capless)
wm8960->set_bias_level = wm8960_set_bias_level_capless;
}
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
.suspend = wm8960_suspend,
.resume = wm8960_resume,
.set_bias_level = wm8960_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8960_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8960_reg,
+};
+
+static const struct regmap_config wm8960_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+ .max_register = WM8960_PLL4,
+
+ .reg_defaults = wm8960_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8960_volatile,
};
static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
int ret;
if (wm8960 == NULL)
return -ENOMEM;
+ wm8960->regmap = regmap_init_i2c(i2c, &wm8960_regmap);
+ if (IS_ERR(wm8960->regmap))
+ return PTR_ERR(wm8960->regmap);
+
+ if (pdata && pdata->shared_lrclk) {
+ ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
+ 0x4, 0x4);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable LRCM: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
i2c_set_clientdata(i2c, wm8960);
- wm8960->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8960, &wm8960_dai, 1);
.id_table = wm8960_i2c_id,
};
-static int __init wm8960_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8960_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8960_modinit);
-
-static void __exit wm8960_exit(void)
-{
- i2c_del_driver(&wm8960_i2c_driver);
-}
-module_exit(wm8960_exit);
+module_i2c_driver(wm8960_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8960 driver");
MODULE_AUTHOR("Liam Girdwood");
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#define WM8961_MAX_REGISTER 0xFC
-static u16 wm8961_reg_defaults[] = {
- 0x009F, /* R0 - Left Input volume */
- 0x009F, /* R1 - Right Input volume */
- 0x0000, /* R2 - LOUT1 volume */
- 0x0000, /* R3 - ROUT1 volume */
- 0x0020, /* R4 - Clocking1 */
- 0x0008, /* R5 - ADC & DAC Control 1 */
- 0x0000, /* R6 - ADC & DAC Control 2 */
- 0x000A, /* R7 - Audio Interface 0 */
- 0x01F4, /* R8 - Clocking2 */
- 0x0000, /* R9 - Audio Interface 1 */
- 0x00FF, /* R10 - Left DAC volume */
- 0x00FF, /* R11 - Right DAC volume */
- 0x0000, /* R12 */
- 0x0000, /* R13 */
- 0x0040, /* R14 - Audio Interface 2 */
- 0x0000, /* R15 - Software Reset */
- 0x0000, /* R16 */
- 0x007B, /* R17 - ALC1 */
- 0x0000, /* R18 - ALC2 */
- 0x0032, /* R19 - ALC3 */
- 0x0000, /* R20 - Noise Gate */
- 0x00C0, /* R21 - Left ADC volume */
- 0x00C0, /* R22 - Right ADC volume */
- 0x0120, /* R23 - Additional control(1) */
- 0x0000, /* R24 - Additional control(2) */
- 0x0000, /* R25 - Pwr Mgmt (1) */
- 0x0000, /* R26 - Pwr Mgmt (2) */
- 0x0000, /* R27 - Additional Control (3) */
- 0x0000, /* R28 - Anti-pop */
- 0x0000, /* R29 */
- 0x005F, /* R30 - Clocking 3 */
- 0x0000, /* R31 */
- 0x0000, /* R32 - ADCL signal path */
- 0x0000, /* R33 - ADCR signal path */
- 0x0000, /* R34 */
- 0x0000, /* R35 */
- 0x0000, /* R36 */
- 0x0000, /* R37 */
- 0x0000, /* R38 */
- 0x0000, /* R39 */
- 0x0000, /* R40 - LOUT2 volume */
- 0x0000, /* R41 - ROUT2 volume */
- 0x0000, /* R42 */
- 0x0000, /* R43 */
- 0x0000, /* R44 */
- 0x0000, /* R45 */
- 0x0000, /* R46 */
- 0x0000, /* R47 - Pwr Mgmt (3) */
- 0x0023, /* R48 - Additional Control (4) */
- 0x0000, /* R49 - Class D Control 1 */
- 0x0000, /* R50 */
- 0x0003, /* R51 - Class D Control 2 */
- 0x0000, /* R52 */
- 0x0000, /* R53 */
- 0x0000, /* R54 */
- 0x0000, /* R55 */
- 0x0106, /* R56 - Clocking 4 */
- 0x0000, /* R57 - DSP Sidetone 0 */
- 0x0000, /* R58 - DSP Sidetone 1 */
- 0x0000, /* R59 */
- 0x0000, /* R60 - DC Servo 0 */
- 0x0000, /* R61 - DC Servo 1 */
- 0x0000, /* R62 */
- 0x015E, /* R63 - DC Servo 3 */
- 0x0010, /* R64 */
- 0x0010, /* R65 - DC Servo 5 */
- 0x0000, /* R66 */
- 0x0001, /* R67 */
- 0x0003, /* R68 - Analogue PGA Bias */
- 0x0000, /* R69 - Analogue HP 0 */
- 0x0060, /* R70 */
- 0x01FB, /* R71 - Analogue HP 2 */
- 0x0000, /* R72 - Charge Pump 1 */
- 0x0065, /* R73 */
- 0x005F, /* R74 */
- 0x0059, /* R75 */
- 0x006B, /* R76 */
- 0x0038, /* R77 */
- 0x000C, /* R78 */
- 0x000A, /* R79 */
- 0x006B, /* R80 */
- 0x0000, /* R81 */
- 0x0000, /* R82 - Charge Pump B */
- 0x0087, /* R83 */
- 0x0000, /* R84 */
- 0x005C, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 - Write Sequencer 1 */
- 0x0000, /* R88 - Write Sequencer 2 */
- 0x0000, /* R89 - Write Sequencer 3 */
- 0x0000, /* R90 - Write Sequencer 4 */
- 0x0000, /* R91 - Write Sequencer 5 */
- 0x0000, /* R92 - Write Sequencer 6 */
- 0x0000, /* R93 - Write Sequencer 7 */
- 0x0000, /* R94 */
- 0x0000, /* R95 */
- 0x0000, /* R96 */
- 0x0000, /* R97 */
- 0x0000, /* R98 */
- 0x0000, /* R99 */
- 0x0000, /* R100 */
- 0x0000, /* R101 */
- 0x0000, /* R102 */
- 0x0000, /* R103 */
- 0x0000, /* R104 */
- 0x0000, /* R105 */
- 0x0000, /* R106 */
- 0x0000, /* R107 */
- 0x0000, /* R108 */
- 0x0000, /* R109 */
- 0x0000, /* R110 */
- 0x0000, /* R111 */
- 0x0000, /* R112 */
- 0x0000, /* R113 */
- 0x0000, /* R114 */
- 0x0000, /* R115 */
- 0x0000, /* R116 */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0000, /* R124 */
- 0x0000, /* R125 */
- 0x0000, /* R126 */
- 0x0000, /* R127 */
- 0x0000, /* R128 */
- 0x0000, /* R129 */
- 0x0000, /* R130 */
- 0x0000, /* R131 */
- 0x0000, /* R132 */
- 0x0000, /* R133 */
- 0x0000, /* R134 */
- 0x0000, /* R135 */
- 0x0000, /* R136 */
- 0x0000, /* R137 */
- 0x0000, /* R138 */
- 0x0000, /* R139 */
- 0x0000, /* R140 */
- 0x0000, /* R141 */
- 0x0000, /* R142 */
- 0x0000, /* R143 */
- 0x0000, /* R144 */
- 0x0000, /* R145 */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x0000, /* R152 */
- 0x0000, /* R153 */
- 0x0000, /* R154 */
- 0x0000, /* R155 */
- 0x0000, /* R156 */
- 0x0000, /* R157 */
- 0x0000, /* R158 */
- 0x0000, /* R159 */
- 0x0000, /* R160 */
- 0x0000, /* R161 */
- 0x0000, /* R162 */
- 0x0000, /* R163 */
- 0x0000, /* R164 */
- 0x0000, /* R165 */
- 0x0000, /* R166 */
- 0x0000, /* R167 */
- 0x0000, /* R168 */
- 0x0000, /* R169 */
- 0x0000, /* R170 */
- 0x0000, /* R171 */
- 0x0000, /* R172 */
- 0x0000, /* R173 */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 */
- 0x0000, /* R177 */
- 0x0000, /* R178 */
- 0x0000, /* R179 */
- 0x0000, /* R180 */
- 0x0000, /* R181 */
- 0x0000, /* R182 */
- 0x0000, /* R183 */
- 0x0000, /* R184 */
- 0x0000, /* R185 */
- 0x0000, /* R186 */
- 0x0000, /* R187 */
- 0x0000, /* R188 */
- 0x0000, /* R189 */
- 0x0000, /* R190 */
- 0x0000, /* R191 */
- 0x0000, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x0000, /* R195 */
- 0x0030, /* R196 */
- 0x0006, /* R197 */
- 0x0000, /* R198 */
- 0x0060, /* R199 */
- 0x0000, /* R200 */
- 0x003F, /* R201 */
- 0x0000, /* R202 */
- 0x0000, /* R203 */
- 0x0000, /* R204 */
- 0x0001, /* R205 */
- 0x0000, /* R206 */
- 0x0181, /* R207 */
- 0x0005, /* R208 */
- 0x0008, /* R209 */
- 0x0008, /* R210 */
- 0x0000, /* R211 */
- 0x013B, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 */
- 0x0000, /* R216 */
- 0x0070, /* R217 */
- 0x0000, /* R218 */
- 0x0000, /* R219 */
- 0x0000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0003, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 */
- 0x0001, /* R226 */
- 0x0008, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0x0000, /* R230 */
- 0x0000, /* R231 */
- 0x0004, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0080, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0052, /* R245 */
- 0x0110, /* R246 */
- 0x0040, /* R247 */
- 0x0000, /* R248 */
- 0x0030, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0001, /* R252 - General test 1 */
+static const struct reg_default wm8961_reg_defaults[] = {
+ { 0, 0x009F }, /* R0 - Left Input volume */
+ { 1, 0x009F }, /* R1 - Right Input volume */
+ { 2, 0x0000 }, /* R2 - LOUT1 volume */
+ { 3, 0x0000 }, /* R3 - ROUT1 volume */
+ { 4, 0x0020 }, /* R4 - Clocking1 */
+ { 5, 0x0008 }, /* R5 - ADC & DAC Control 1 */
+ { 6, 0x0000 }, /* R6 - ADC & DAC Control 2 */
+ { 7, 0x000A }, /* R7 - Audio Interface 0 */
+ { 8, 0x01F4 }, /* R8 - Clocking2 */
+ { 9, 0x0000 }, /* R9 - Audio Interface 1 */
+ { 10, 0x00FF }, /* R10 - Left DAC volume */
+ { 11, 0x00FF }, /* R11 - Right DAC volume */
+
+ { 14, 0x0040 }, /* R14 - Audio Interface 2 */
+
+ { 17, 0x007B }, /* R17 - ALC1 */
+ { 18, 0x0000 }, /* R18 - ALC2 */
+ { 19, 0x0032 }, /* R19 - ALC3 */
+ { 20, 0x0000 }, /* R20 - Noise Gate */
+ { 21, 0x00C0 }, /* R21 - Left ADC volume */
+ { 22, 0x00C0 }, /* R22 - Right ADC volume */
+ { 23, 0x0120 }, /* R23 - Additional control(1) */
+ { 24, 0x0000 }, /* R24 - Additional control(2) */
+ { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */
+ { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */
+ { 27, 0x0000 }, /* R27 - Additional Control (3) */
+ { 28, 0x0000 }, /* R28 - Anti-pop */
+
+ { 30, 0x005F }, /* R30 - Clocking 3 */
+
+ { 32, 0x0000 }, /* R32 - ADCL signal path */
+ { 33, 0x0000 }, /* R33 - ADCR signal path */
+
+ { 40, 0x0000 }, /* R40 - LOUT2 volume */
+ { 41, 0x0000 }, /* R41 - ROUT2 volume */
+
+ { 47, 0x0000 }, /* R47 - Pwr Mgmt (3) */
+ { 48, 0x0023 }, /* R48 - Additional Control (4) */
+ { 49, 0x0000 }, /* R49 - Class D Control 1 */
+
+ { 51, 0x0003 }, /* R51 - Class D Control 2 */
+
+ { 56, 0x0106 }, /* R56 - Clocking 4 */
+ { 57, 0x0000 }, /* R57 - DSP Sidetone 0 */
+ { 58, 0x0000 }, /* R58 - DSP Sidetone 1 */
+
+ { 60, 0x0000 }, /* R60 - DC Servo 0 */
+ { 61, 0x0000 }, /* R61 - DC Servo 1 */
+
+ { 63, 0x015E }, /* R63 - DC Servo 3 */
+
+ { 65, 0x0010 }, /* R65 - DC Servo 5 */
+
+ { 68, 0x0003 }, /* R68 - Analogue PGA Bias */
+ { 69, 0x0000 }, /* R69 - Analogue HP 0 */
+
+ { 71, 0x01FB }, /* R71 - Analogue HP 2 */
+ { 72, 0x0000 }, /* R72 - Charge Pump 1 */
+
+ { 82, 0x0000 }, /* R82 - Charge Pump B */
+
+ { 87, 0x0000 }, /* R87 - Write Sequencer 1 */
+ { 88, 0x0000 }, /* R88 - Write Sequencer 2 */
+ { 89, 0x0000 }, /* R89 - Write Sequencer 3 */
+ { 90, 0x0000 }, /* R90 - Write Sequencer 4 */
+ { 91, 0x0000 }, /* R91 - Write Sequencer 5 */
+ { 92, 0x0000 }, /* R92 - Write Sequencer 6 */
+ { 93, 0x0000 }, /* R93 - Write Sequencer 7 */
+
+ { 252, 0x0001 }, /* R252 - General test 1 */
};
struct wm8961_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
int sysclk;
};
-static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
+static bool wm8961_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8961_SOFTWARE_RESET:
case WM8961_WRITE_SEQUENCER_7:
case WM8961_DC_SERVO_1:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
-static int wm8961_reset(struct snd_soc_codec *codec)
+static bool wm8961_readable(struct device *dev, unsigned int reg)
{
- return snd_soc_write(codec, WM8961_SOFTWARE_RESET, 0);
+ switch (reg) {
+ case WM8961_LEFT_INPUT_VOLUME:
+ case WM8961_RIGHT_INPUT_VOLUME:
+ case WM8961_LOUT1_VOLUME:
+ case WM8961_ROUT1_VOLUME:
+ case WM8961_CLOCKING1:
+ case WM8961_ADC_DAC_CONTROL_1:
+ case WM8961_ADC_DAC_CONTROL_2:
+ case WM8961_AUDIO_INTERFACE_0:
+ case WM8961_CLOCKING2:
+ case WM8961_AUDIO_INTERFACE_1:
+ case WM8961_LEFT_DAC_VOLUME:
+ case WM8961_RIGHT_DAC_VOLUME:
+ case WM8961_AUDIO_INTERFACE_2:
+ case WM8961_SOFTWARE_RESET:
+ case WM8961_ALC1:
+ case WM8961_ALC2:
+ case WM8961_ALC3:
+ case WM8961_NOISE_GATE:
+ case WM8961_LEFT_ADC_VOLUME:
+ case WM8961_RIGHT_ADC_VOLUME:
+ case WM8961_ADDITIONAL_CONTROL_1:
+ case WM8961_ADDITIONAL_CONTROL_2:
+ case WM8961_PWR_MGMT_1:
+ case WM8961_PWR_MGMT_2:
+ case WM8961_ADDITIONAL_CONTROL_3:
+ case WM8961_ANTI_POP:
+ case WM8961_CLOCKING_3:
+ case WM8961_ADCL_SIGNAL_PATH:
+ case WM8961_ADCR_SIGNAL_PATH:
+ case WM8961_LOUT2_VOLUME:
+ case WM8961_ROUT2_VOLUME:
+ case WM8961_PWR_MGMT_3:
+ case WM8961_ADDITIONAL_CONTROL_4:
+ case WM8961_CLASS_D_CONTROL_1:
+ case WM8961_CLASS_D_CONTROL_2:
+ case WM8961_CLOCKING_4:
+ case WM8961_DSP_SIDETONE_0:
+ case WM8961_DSP_SIDETONE_1:
+ case WM8961_DC_SERVO_0:
+ case WM8961_DC_SERVO_1:
+ case WM8961_DC_SERVO_3:
+ case WM8961_DC_SERVO_5:
+ case WM8961_ANALOGUE_PGA_BIAS:
+ case WM8961_ANALOGUE_HP_0:
+ case WM8961_ANALOGUE_HP_2:
+ case WM8961_CHARGE_PUMP_1:
+ case WM8961_CHARGE_PUMP_B:
+ case WM8961_WRITE_SEQUENCER_1:
+ case WM8961_WRITE_SEQUENCER_2:
+ case WM8961_WRITE_SEQUENCER_3:
+ case WM8961_WRITE_SEQUENCER_4:
+ case WM8961_WRITE_SEQUENCER_5:
+ case WM8961_WRITE_SEQUENCER_6:
+ case WM8961_WRITE_SEQUENCER_7:
+ case WM8961_GENERAL_TEST_1:
+ return true;
+ default:
+ return false;
+ }
}
/*
int ret = 0;
u16 reg;
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
- reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET);
- if (reg != 0x1801) {
- dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
- return -EINVAL;
- }
-
- /* This isn't volatile - readback doesn't correspond to write */
- codec->cache_bypass = 1;
- reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME);
- codec->cache_bypass = 0;
- dev_info(codec->dev, "WM8961 family %d revision %c\n",
- (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
- ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
- + 'A');
-
- ret = wm8961_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
- }
-
/* Enable class W */
reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
reg |= WM8961_CP_DYN_PWR_MASK;
.suspend = wm8961_suspend,
.resume = wm8961_resume,
.set_bias_level = wm8961_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8961_reg_defaults),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8961_reg_defaults,
- .volatile_register = wm8961_volatile_register,
+};
+
+static const struct regmap_config wm8961_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = WM8961_MAX_REGISTER,
+
+ .reg_defaults = wm8961_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = wm8961_volatile,
+ .readable_reg = wm8961_readable,
};
static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8961_priv *wm8961;
+ unsigned int val;
int ret;
wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv),
if (wm8961 == NULL)
return -ENOMEM;
+ wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap);
+ if (IS_ERR(wm8961->regmap))
+ return PTR_ERR(wm8961->regmap);
+
+ ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+
+ if (val != 0x1801) {
+ dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val);
+ return -EINVAL;
+ }
+
+ /* This isn't volatile - readback doesn't correspond to write */
+ regcache_cache_bypass(wm8961->regmap, true);
+ ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val);
+ regcache_cache_bypass(wm8961->regmap, false);
+
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(&i2c->dev, "WM8961 family %d revision %c\n",
+ (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
+ ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+ + 'A');
+
+ ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8961);
ret = snd_soc_register_codec(&i2c->dev,
.id_table = wm8961_i2c_id,
};
-static int __init wm8961_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8961_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8961 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8961_modinit);
-
-static void __exit wm8961_exit(void)
-{
- i2c_del_driver(&wm8961_i2c_driver);
-}
-module_exit(wm8961_exit);
+module_i2c_driver(wm8961_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8961 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
.id_table = wm8971_i2c_id,
};
-static int __init wm8971_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8971_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8971 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8971_modinit);
-
-static void __exit wm8971_exit(void)
-{
- i2c_del_driver(&wm8971_i2c_driver);
-}
-module_exit(wm8971_exit);
+module_i2c_driver(wm8971_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8971 driver");
MODULE_AUTHOR("Lab126");
.id_table = wm8974_i2c_id,
};
-static int __init wm8974_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8974_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8974 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8974_modinit);
-
-static void __exit wm8974_exit(void)
-{
- i2c_del_driver(&wm8974_i2c_driver);
-}
-module_exit(wm8974_exit);
+module_i2c_driver(wm8974_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8974 driver");
MODULE_AUTHOR("Liam Girdwood");
.id_table = wm8978_i2c_id,
};
-static int __init wm8978_modinit(void)
-{
- int ret = 0;
- ret = i2c_add_driver(&wm8978_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8978 I2C driver: %d\n",
- ret);
- }
- return ret;
-}
-module_init(wm8978_modinit);
-
-static void __exit wm8978_exit(void)
-{
- i2c_del_driver(&wm8978_i2c_driver);
-}
-module_exit(wm8978_exit);
+module_i2c_driver(wm8978_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8978 codec driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "wm8983.h"
-static const u16 wm8983_reg_defs[WM8983_MAX_REGISTER + 1] = {
- [0x00] = 0x0000, /* R0 - Software Reset */
- [0x01] = 0x0000, /* R1 - Power management 1 */
- [0x02] = 0x0000, /* R2 - Power management 2 */
- [0x03] = 0x0000, /* R3 - Power management 3 */
- [0x04] = 0x0050, /* R4 - Audio Interface */
- [0x05] = 0x0000, /* R5 - Companding control */
- [0x06] = 0x0140, /* R6 - Clock Gen control */
- [0x07] = 0x0000, /* R7 - Additional control */
- [0x08] = 0x0000, /* R8 - GPIO Control */
- [0x09] = 0x0000, /* R9 - Jack Detect Control 1 */
- [0x0A] = 0x0000, /* R10 - DAC Control */
- [0x0B] = 0x00FF, /* R11 - Left DAC digital Vol */
- [0x0C] = 0x00FF, /* R12 - Right DAC digital vol */
- [0x0D] = 0x0000, /* R13 - Jack Detect Control 2 */
- [0x0E] = 0x0100, /* R14 - ADC Control */
- [0x0F] = 0x00FF, /* R15 - Left ADC Digital Vol */
- [0x10] = 0x00FF, /* R16 - Right ADC Digital Vol */
- [0x12] = 0x012C, /* R18 - EQ1 - low shelf */
- [0x13] = 0x002C, /* R19 - EQ2 - peak 1 */
- [0x14] = 0x002C, /* R20 - EQ3 - peak 2 */
- [0x15] = 0x002C, /* R21 - EQ4 - peak 3 */
- [0x16] = 0x002C, /* R22 - EQ5 - high shelf */
- [0x18] = 0x0032, /* R24 - DAC Limiter 1 */
- [0x19] = 0x0000, /* R25 - DAC Limiter 2 */
- [0x1B] = 0x0000, /* R27 - Notch Filter 1 */
- [0x1C] = 0x0000, /* R28 - Notch Filter 2 */
- [0x1D] = 0x0000, /* R29 - Notch Filter 3 */
- [0x1E] = 0x0000, /* R30 - Notch Filter 4 */
- [0x20] = 0x0038, /* R32 - ALC control 1 */
- [0x21] = 0x000B, /* R33 - ALC control 2 */
- [0x22] = 0x0032, /* R34 - ALC control 3 */
- [0x23] = 0x0000, /* R35 - Noise Gate */
- [0x24] = 0x0008, /* R36 - PLL N */
- [0x25] = 0x000C, /* R37 - PLL K 1 */
- [0x26] = 0x0093, /* R38 - PLL K 2 */
- [0x27] = 0x00E9, /* R39 - PLL K 3 */
- [0x29] = 0x0000, /* R41 - 3D control */
- [0x2A] = 0x0000, /* R42 - OUT4 to ADC */
- [0x2B] = 0x0000, /* R43 - Beep control */
- [0x2C] = 0x0033, /* R44 - Input ctrl */
- [0x2D] = 0x0010, /* R45 - Left INP PGA gain ctrl */
- [0x2E] = 0x0010, /* R46 - Right INP PGA gain ctrl */
- [0x2F] = 0x0100, /* R47 - Left ADC BOOST ctrl */
- [0x30] = 0x0100, /* R48 - Right ADC BOOST ctrl */
- [0x31] = 0x0002, /* R49 - Output ctrl */
- [0x32] = 0x0001, /* R50 - Left mixer ctrl */
- [0x33] = 0x0001, /* R51 - Right mixer ctrl */
- [0x34] = 0x0039, /* R52 - LOUT1 (HP) volume ctrl */
- [0x35] = 0x0039, /* R53 - ROUT1 (HP) volume ctrl */
- [0x36] = 0x0039, /* R54 - LOUT2 (SPK) volume ctrl */
- [0x37] = 0x0039, /* R55 - ROUT2 (SPK) volume ctrl */
- [0x38] = 0x0001, /* R56 - OUT3 mixer ctrl */
- [0x39] = 0x0001, /* R57 - OUT4 (MONO) mix ctrl */
- [0x3D] = 0x0000 /* R61 - BIAS CTRL */
+static const struct reg_default wm8983_defaults[] = {
+ { 0x01, 0x0000 }, /* R1 - Power management 1 */
+ { 0x02, 0x0000 }, /* R2 - Power management 2 */
+ { 0x03, 0x0000 }, /* R3 - Power management 3 */
+ { 0x04, 0x0050 }, /* R4 - Audio Interface */
+ { 0x05, 0x0000 }, /* R5 - Companding control */
+ { 0x06, 0x0140 }, /* R6 - Clock Gen control */
+ { 0x07, 0x0000 }, /* R7 - Additional control */
+ { 0x08, 0x0000 }, /* R8 - GPIO Control */
+ { 0x09, 0x0000 }, /* R9 - Jack Detect Control 1 */
+ { 0x0A, 0x0000 }, /* R10 - DAC Control */
+ { 0x0B, 0x00FF }, /* R11 - Left DAC digital Vol */
+ { 0x0C, 0x00FF }, /* R12 - Right DAC digital vol */
+ { 0x0D, 0x0000 }, /* R13 - Jack Detect Control 2 */
+ { 0x0E, 0x0100 }, /* R14 - ADC Control */
+ { 0x0F, 0x00FF }, /* R15 - Left ADC Digital Vol */
+ { 0x10, 0x00FF }, /* R16 - Right ADC Digital Vol */
+ { 0x12, 0x012C }, /* R18 - EQ1 - low shelf */
+ { 0x13, 0x002C }, /* R19 - EQ2 - peak 1 */
+ { 0x14, 0x002C }, /* R20 - EQ3 - peak 2 */
+ { 0x15, 0x002C }, /* R21 - EQ4 - peak 3 */
+ { 0x16, 0x002C }, /* R22 - EQ5 - high shelf */
+ { 0x18, 0x0032 }, /* R24 - DAC Limiter 1 */
+ { 0x19, 0x0000 }, /* R25 - DAC Limiter 2 */
+ { 0x1B, 0x0000 }, /* R27 - Notch Filter 1 */
+ { 0x1C, 0x0000 }, /* R28 - Notch Filter 2 */
+ { 0x1D, 0x0000 }, /* R29 - Notch Filter 3 */
+ { 0x1E, 0x0000 }, /* R30 - Notch Filter 4 */
+ { 0x20, 0x0038 }, /* R32 - ALC control 1 */
+ { 0x21, 0x000B }, /* R33 - ALC control 2 */
+ { 0x22, 0x0032 }, /* R34 - ALC control 3 */
+ { 0x23, 0x0000 }, /* R35 - Noise Gate */
+ { 0x24, 0x0008 }, /* R36 - PLL N */
+ { 0x25, 0x000C }, /* R37 - PLL K 1 */
+ { 0x26, 0x0093 }, /* R38 - PLL K 2 */
+ { 0x27, 0x00E9 }, /* R39 - PLL K 3 */
+ { 0x29, 0x0000 }, /* R41 - 3D control */
+ { 0x2A, 0x0000 }, /* R42 - OUT4 to ADC */
+ { 0x2B, 0x0000 }, /* R43 - Beep control */
+ { 0x2C, 0x0033 }, /* R44 - Input ctrl */
+ { 0x2D, 0x0010 }, /* R45 - Left INP PGA gain ctrl */
+ { 0x2E, 0x0010 }, /* R46 - Right INP PGA gain ctrl */
+ { 0x2F, 0x0100 }, /* R47 - Left ADC BOOST ctrl */
+ { 0x30, 0x0100 }, /* R48 - Right ADC BOOST ctrl */
+ { 0x31, 0x0002 }, /* R49 - Output ctrl */
+ { 0x32, 0x0001 }, /* R50 - Left mixer ctrl */
+ { 0x33, 0x0001 }, /* R51 - Right mixer ctrl */
+ { 0x34, 0x0039 }, /* R52 - LOUT1 (HP) volume ctrl */
+ { 0x35, 0x0039 }, /* R53 - ROUT1 (HP) volume ctrl */
+ { 0x36, 0x0039 }, /* R54 - LOUT2 (SPK) volume ctrl */
+ { 0x37, 0x0039 }, /* R55 - ROUT2 (SPK) volume ctrl */
+ { 0x38, 0x0001 }, /* R56 - OUT3 mixer ctrl */
+ { 0x39, 0x0001 }, /* R57 - OUT4 (MONO) mix ctrl */
+ { 0x3D, 0x0000 }, /* R61 - BIAS CTRL */
};
static const struct wm8983_reg_access {
};
struct wm8983_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
u32 sysclk;
u32 bclk;
};
return 0;
}
-static int wm8983_readable(struct snd_soc_codec *codec, unsigned int reg)
+static bool wm8983_readable(struct device *dev, unsigned int reg)
{
if (reg > WM8983_MAX_REGISTER)
return 0;
static int wm8983_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- ret = snd_soc_cache_sync(codec);
+ ret = regcache_sync(wm8983->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
return ret;
static int wm8983_probe(struct snd_soc_codec *codec)
{
int ret;
- struct wm8983_priv *wm8983 = snd_soc_codec_get_drvdata(codec);
int i;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8983->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
return ret;
.suspend = wm8983_suspend,
.resume = wm8983_resume,
.set_bias_level = wm8983_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8983_reg_defs),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8983_reg_defs,
.controls = wm8983_snd_controls,
.num_controls = ARRAY_SIZE(wm8983_snd_controls),
.dapm_widgets = wm8983_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets),
.dapm_routes = wm8983_audio_map,
.num_dapm_routes = ARRAY_SIZE(wm8983_audio_map),
- .readable_register = wm8983_readable
+};
+
+static const struct regmap_config wm8983_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .reg_defaults = wm8983_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8983_defaults),
+ .cache_type = REGCACHE_RBTREE,
+
+ .readable_reg = wm8983_readable,
};
#if defined(CONFIG_SPI_MASTER)
struct wm8983_priv *wm8983;
int ret;
- wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL);
+ wm8983 = devm_kzalloc(&spi->dev, sizeof *wm8983, GFP_KERNEL);
if (!wm8983)
return -ENOMEM;
- wm8983->control_type = SND_SOC_SPI;
+ wm8983->regmap = devm_regmap_init_spi(spi, &wm8983_regmap);
+ if (IS_ERR(wm8983->regmap)) {
+ ret = PTR_ERR(wm8983->regmap);
+ dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
spi_set_drvdata(spi, wm8983);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_wm8983, &wm8983_dai, 1);
- if (ret < 0)
- kfree(wm8983);
return ret;
}
static int __devexit wm8983_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
- kfree(spi_get_drvdata(spi));
return 0;
}
struct wm8983_priv *wm8983;
int ret;
- wm8983 = kzalloc(sizeof *wm8983, GFP_KERNEL);
+ wm8983 = devm_kzalloc(&i2c->dev, sizeof *wm8983, GFP_KERNEL);
if (!wm8983)
return -ENOMEM;
- wm8983->control_type = SND_SOC_I2C;
+ wm8983->regmap = devm_regmap_init_i2c(i2c, &wm8983_regmap);
+ if (IS_ERR(wm8983->regmap)) {
+ ret = PTR_ERR(wm8983->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
i2c_set_clientdata(i2c, wm8983);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8983, &wm8983_dai, 1);
- if (ret < 0)
- kfree(wm8983);
+
return ret;
}
static __devexit int wm8983_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
return 0;
}
struct wm8990_priv *wm8990;
int ret;
- wm8990 = kzalloc(sizeof(struct wm8990_priv), GFP_KERNEL);
+ wm8990 = devm_kzalloc(&i2c->dev, sizeof(struct wm8990_priv),
+ GFP_KERNEL);
if (wm8990 == NULL)
return -ENOMEM;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8990, &wm8990_dai, 1);
- if (ret < 0)
- kfree(wm8990);
+
return ret;
}
static __devexit int wm8990_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
+
return 0;
}
struct wm8991_priv *wm8991;
int ret;
- wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL);
+ wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL);
if (!wm8991)
return -ENOMEM;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8991, &wm8991_dai, 1);
- if (ret < 0)
- kfree(wm8991);
+
return ret;
}
static __devexit int wm8991_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
- kfree(i2c_get_clientdata(client));
+
return 0;
}
.id_table = wm8991_i2c_id,
};
-static int __init wm8991_modinit(void)
-{
- int ret;
- ret = i2c_add_driver(&wm8991_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n",
- ret);
- }
- return 0;
-}
-module_init(wm8991_modinit);
-
-static void __exit wm8991_exit(void)
-{
- i2c_del_driver(&wm8991_i2c_driver);
-}
-module_exit(wm8991_exit);
+module_i2c_driver(wm8991_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8991 driver");
MODULE_AUTHOR("Graeme Gregory");
wm8993->pdata.lineout2fb,
wm8993->pdata.jd_scthr,
wm8993->pdata.jd_thr,
+ wm8993->pdata.micbias1_delay,
+ wm8993->pdata.micbias2_delay,
wm8993->pdata.micbias1_lvl,
wm8993->pdata.micbias2_lvl);
eq_tlv),
};
+static const struct snd_kcontrol_new wm8994_drc_controls[] = {
+SND_SOC_BYTES_MASK("AIF1.1 DRC", WM8994_AIF1_DRC1_1, 5,
+ WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+ WM8994_AIF1ADC1R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF1.2 DRC", WM8994_AIF1_DRC2_1, 5,
+ WM8994_AIF1DAC2_DRC_ENA | WM8994_AIF1ADC2L_DRC_ENA |
+ WM8994_AIF1ADC2R_DRC_ENA),
+SND_SOC_BYTES_MASK("AIF2 DRC", WM8994_AIF2_DRC_1, 5,
+ WM8994_AIF2DAC_DRC_ENA | WM8994_AIF2ADCL_DRC_ENA |
+ WM8994_AIF2ADCR_DRC_ENA),
+};
+
static const char *wm8958_ng_text[] = {
"30ms", "125ms", "250ms", "500ms",
};
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
return configure_clock(codec);
+ case SND_SOC_DAPM_POST_PMU:
+ /*
+ * JACKDET won't run until we start the clock and it
+ * only reports deltas, make sure we notify the state
+ * up the stack on startup. Use a *very* generous
+ * timeout for paranoia, there's no urgency and we
+ * don't want false reports.
+ */
+ if (wm8994->jackdet && !wm8994->clk_has_run) {
+ schedule_delayed_work(&wm8994->jackdet_bootstrap,
+ msecs_to_jiffies(1000));
+ wm8994->clk_has_run = true;
+ }
+ break;
+
case SND_SOC_DAPM_POST_PMD:
configure_clock(codec);
break;
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, 3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, 2, 0, NULL, 0),
case WM8994_FLL_SRC_LRCLK:
case WM8994_FLL_SRC_BCLK:
break;
+ case WM8994_FLL_SRC_INTERNAL:
+ freq_in = 12000000;
+ freq_out = 12000000;
+ break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
WM8994_FLL1_N_MASK,
- fll.n << WM8994_FLL1_N_SHIFT);
+ fll.n << WM8994_FLL1_N_SHIFT);
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
- WM8958_FLL1_BYP |
+ WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK |
WM8994_FLL1_REFCLK_SRC_MASK,
+ ((src == WM8994_FLL_SRC_INTERNAL)
+ << WM8994_FLL1_FRC_NCO_SHIFT) |
(fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
(src - 1));
}
}
+ reg = WM8994_FLL1_ENA;
+
if (fll.k)
- reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
- else
- reg = WM8994_FLL1_ENA;
+ reg |= WM8994_FLL1_FRAC;
+ if (src == WM8994_FLL_SRC_INTERNAL)
+ reg |= WM8994_FLL1_OSC_ENA;
+
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
- WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
- reg);
+ WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA |
+ WM8994_FLL1_FRAC, reg);
if (wm8994->fll_locked_irq) {
timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
{
- struct snd_soc_codec *codec = wm8994->codec;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
struct wm8994_pdata *pdata = wm8994->pdata;
struct snd_kcontrol_new controls[] = {
SOC_ENUM_EXT("AIF1.1 EQ Mode",
wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, controls,
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to add ReTune Mobile controls: %d\n", ret);
}
static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
{
- struct snd_soc_codec *codec = wm8994->codec;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
struct wm8994_pdata *pdata = wm8994->pdata;
int ret, i;
pdata->lineout2fb,
pdata->jd_scthr,
pdata->jd_thr,
+ pdata->micb1_delay,
+ pdata->micb2_delay,
pdata->micbias1_lvl,
pdata->micbias2_lvl);
};
/* We need an array of texts for the enum API */
- wm8994->drc_texts = devm_kzalloc(wm8994->codec->dev,
+ wm8994->drc_texts = devm_kzalloc(wm8994->hubs.codec->dev,
sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL);
if (!wm8994->drc_texts) {
- dev_err(wm8994->codec->dev,
+ dev_err(wm8994->hubs.codec->dev,
"Failed to allocate %d DRC config texts\n",
pdata->num_drc_cfgs);
return;
wm8994->drc_enum.max = pdata->num_drc_cfgs;
wm8994->drc_enum.texts = wm8994->drc_texts;
- ret = snd_soc_add_codec_controls(wm8994->codec, controls,
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls,
ARRAY_SIZE(controls));
- if (ret != 0)
- dev_err(wm8994->codec->dev,
- "Failed to add DRC mode controls: %d\n", ret);
-
for (i = 0; i < WM8994_NUM_DRC; i++)
wm8994_set_drc(codec, i);
+ } else {
+ ret = snd_soc_add_codec_controls(wm8994->hubs.codec,
+ wm8994_drc_controls,
+ ARRAY_SIZE(wm8994_drc_controls));
}
+ if (ret != 0)
+ dev_err(wm8994->hubs.codec->dev,
+ "Failed to add DRC mode controls: %d\n", ret);
+
+
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994);
else
- snd_soc_add_codec_controls(wm8994->codec, wm8994_eq_controls,
+ snd_soc_add_codec_controls(wm8994->hubs.codec, wm8994_eq_controls,
ARRAY_SIZE(wm8994_eq_controls));
for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg);
+ /* enable MICDET and MICSHRT deboune */
+ snd_soc_update_bits(codec, WM8994_IRQ_DEBOUNCE,
+ WM8994_MIC1_DET_DB_MASK | WM8994_MIC1_SHRT_DB_MASK |
+ WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK,
+ WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB);
+
snd_soc_dapm_sync(&codec->dapm);
return 0;
static irqreturn_t wm8994_mic_irq(int irq, void *data)
{
struct wm8994_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
+ struct snd_soc_codec *codec = priv->hubs.codec;
#ifndef CONFIG_SND_SOC_WM8994_MODULE
trace_snd_soc_jack_irq(dev_name(codec->dev));
snd_soc_jack_report(wm8994->micdet[0].jack, 0,
wm8994->btn_mask |
- SND_JACK_HEADSET);
+ SND_JACK_HEADSET);
}
return;
}
static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
- struct snd_soc_codec *codec = wm8994->codec;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
int reg;
bool present;
SND_JACK_MECHANICAL | SND_JACK_HEADSET |
wm8994->btn_mask);
+ /* Since we only report deltas force an update, ensures we
+ * avoid bootstrapping issues with the core. */
+ snd_soc_jack_report(wm8994->micdet[0].jack, 0, 0);
+
pm_runtime_put(codec->dev);
return IRQ_HANDLED;
}
+static void wm1811_jackdet_bootstrap(struct work_struct *work)
+{
+ struct wm8994_priv *wm8994 = container_of(work,
+ struct wm8994_priv,
+ jackdet_bootstrap.work);
+ wm1811_jackdet_irq(0, wm8994);
+}
+
/**
* wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ
*
* otherwise jump straight to microphone detection.
*/
if (wm8994->jackdet) {
+ /* Disable debounce for the initial detect */
+ snd_soc_update_bits(codec, WM1811_JACKDET_CTRL,
+ WM1811_JACKDET_DB, 0);
+
snd_soc_update_bits(codec, WM8958_MICBIAS2,
WM8958_MICB2_DISCH,
WM8958_MICB2_DISCH);
static irqreturn_t wm8958_mic_irq(int irq, void *data)
{
struct wm8994_priv *wm8994 = data;
- struct snd_soc_codec *codec = wm8994->codec;
+ struct snd_soc_codec *codec = wm8994->hubs.codec;
int reg, count;
/*
unsigned int reg;
int ret, i;
- wm8994->codec = codec;
+ wm8994->hubs.codec = codec;
codec->control_data = control->regmap;
snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
- wm8994->codec = codec;
-
mutex_init(&wm8994->accdet_lock);
INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work);
+ INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
+ wm1811_jackdet_bootstrap);
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);
wm8994->hubs.no_cache_dac_hp_direct = true;
wm8994->fll_byp = true;
- switch (wm8994->revision) {
+ switch (control->cust_id) {
case 0:
- case 1:
case 2:
- case 3:
wm8994->hubs.dcs_codes_l = -9;
wm8994->hubs.dcs_codes_r = -7;
break;
+ case 1:
+ case 3:
+ wm8994->hubs.dcs_codes_l = -8;
+ wm8994->hubs.dcs_codes_r = -7;
+ break;
default:
break;
}
switch (control->type) {
case WM1811:
- if (wm8994->revision > 1) {
+ if (control->cust_id > 1 || wm8994->revision > 1) {
ret = wm8994_request_irq(wm8994->wm8994,
WM8994_IRQ_GPIO(6),
wm1811_jackdet_irq, "JACKDET",
#define WM8994_FLL1 1
#define WM8994_FLL2 2
-#define WM8994_FLL_SRC_MCLK1 1
-#define WM8994_FLL_SRC_MCLK2 2
-#define WM8994_FLL_SRC_LRCLK 3
-#define WM8994_FLL_SRC_BCLK 4
+#define WM8994_FLL_SRC_MCLK1 1
+#define WM8994_FLL_SRC_MCLK2 2
+#define WM8994_FLL_SRC_LRCLK 3
+#define WM8994_FLL_SRC_BCLK 4
+#define WM8994_FLL_SRC_INTERNAL 5
enum wm8994_vmid_mode {
WM8994_VMID_NORMAL,
struct wm8994_priv {
struct wm_hubs_data hubs;
struct wm8994 *wm8994;
- struct snd_soc_codec *codec;
int sysclk[2];
int sysclk_rate[2];
int mclk[2];
struct completion fll_locked[2];
bool fll_locked_irq;
bool fll_byp;
+ bool clk_has_run;
int vmid_refcount;
int active_refcount;
int btn_mask;
bool jackdet;
int jackdet_mode;
+ struct delayed_work jackdet_bootstrap;
wm8958_micdet_cb jack_cb;
void *jack_cb_data;
SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),
-SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
.id_table = wm9090_id,
};
-static int __init wm9090_init(void)
-{
- return i2c_add_driver(&wm9090_i2c_driver);
-}
-module_init(wm9090_init);
-
-static void __exit wm9090_exit(void)
-{
- i2c_del_driver(&wm9090_i2c_driver);
-}
-module_exit(wm9090_exit);
+module_i2c_driver(wm9090_i2c_driver);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("WM9090 ASoC driver");
SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
-SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
-SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
+SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv),
+SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1,
+ boost_tlv),
SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
-SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
+SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
{
int ret = 0;
- codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
static struct platform_driver wm9712_codec_driver = {
.driver = {
- .name = "wm9712-codec",
- .owner = THIS_MODULE,
+ .name = "wm9712-codec",
+ .owner = THIS_MODULE,
},
.probe = wm9712_probe,
if (wm9713 == NULL)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
- codec->control_data = wm9713; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
list_add_tail(&cache->list, &hubs->dcs_cache);
}
+static void wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+ u16 *reg_l, u16 *reg_r)
+{
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+ u16 dcs_reg, reg;
+
+ switch (hubs->dcs_readback_mode) {
+ case 2:
+ dcs_reg = WM8994_DC_SERVO_4E;
+ break;
+ case 1:
+ dcs_reg = WM8994_DC_SERVO_READBACK;
+ break;
+ default:
+ dcs_reg = WM8993_DC_SERVO_3;
+ break;
+ }
+
+ /* Different chips in the family support different readback
+ * methods.
+ */
+ switch (hubs->dcs_readback_mode) {
+ case 0:
+ *reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ & WM8993_DCS_INTEG_CHAN_0_MASK;
+ *reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ & WM8993_DCS_INTEG_CHAN_1_MASK;
+ break;
+ case 2:
+ case 1:
+ reg = snd_soc_read(codec, dcs_reg);
+ *reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ *reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ break;
+ default:
+ WARN(1, "Unknown DCS readback method\n");
+ return;
+ }
+}
+
/*
* Startup calibration of the DC servo
*/
-static void calibrate_dc_servo(struct snd_soc_codec *codec)
+static void enable_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct wm_hubs_dcs_cache *cache;
s8 offset;
- u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
+ u16 reg_l, reg_r, dcs_cfg, dcs_reg;
switch (hubs->dcs_readback_mode) {
case 2:
WM8993_DCS_TRIG_STARTUP_1);
}
- /* Different chips in the family support different readback
- * methods.
- */
- switch (hubs->dcs_readback_mode) {
- case 0:
- reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
- & WM8993_DCS_INTEG_CHAN_0_MASK;
- reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
- & WM8993_DCS_INTEG_CHAN_1_MASK;
- break;
- case 2:
- case 1:
- reg = snd_soc_read(codec, dcs_reg);
- reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
- >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
- reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
- break;
- default:
- WARN(1, "Unknown DCS readback method\n");
- return;
- }
+ wm_hubs_read_dc_servo(codec, ®_l, ®_r);
dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
hubs->dcs_codes_l, hubs->dcs_codes_r);
/* HPOUT1R */
- offset = reg_r;
+ offset = (s8)reg_r;
+ dev_dbg(codec->dev, "DCS right %d->%d\n", offset,
+ offset + hubs->dcs_codes_r);
offset += hubs->dcs_codes_r;
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
/* HPOUT1L */
- offset = reg_l;
+ offset = (s8)reg_l;
+ dev_dbg(codec->dev, "DCS left %d->%d\n", offset,
+ offset + hubs->dcs_codes_l);
offset += hubs->dcs_codes_l;
dcs_cfg |= (u8)offset;
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
- calibrate_dc_servo(codec);
+ enable_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
return 0;
}
+static int micbias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+
+ switch (w->shift) {
+ case WM8993_MICB1_ENA_SHIFT:
+ if (hubs->micb1_delay)
+ msleep(hubs->micb1_delay);
+ break;
+ case WM8993_MICB2_ENA_SHIFT:
+ if (hubs->micb2_delay)
+ msleep(hubs->micb2_delay);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
void wm_hubs_update_class_w(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
snd_soc_update_bits(codec, WM8993_CLASS_W_0,
WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+
+ snd_soc_write(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME));
+ snd_soc_write(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+ snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME));
}
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
SND_SOC_DAPM_INPUT("IN2RN"),
SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
-SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
-SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0,
+ micbias_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
in1l_pga, ARRAY_SIZE(in1l_pga)),
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ hubs->codec = codec;
+
INIT_LIST_HEAD(&hubs->dcs_cache);
init_completion(&hubs->dcs_done);
int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
- int jd_scthr, int jd_thr, int micbias1_lvl,
- int micbias2_lvl)
+ int jd_scthr, int jd_thr,
+ int micbias1_delay, int micbias2_delay,
+ int micbias1_lvl, int micbias2_lvl)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
hubs->lineout1_se = !lineout1_diff;
hubs->lineout2_se = !lineout2_diff;
+ hubs->micb1_delay = micbias1_delay;
+ hubs->micb2_delay = micbias2_delay;
if (!lineout1_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
struct list_head dcs_cache;
bool (*check_class_w_digital)(struct snd_soc_codec *);
+ int micb1_delay;
+ int micb2_delay;
+
bool lineout1_se;
bool lineout1n_ena;
bool lineout1p_ena;
bool dcs_done_irq;
struct completion dcs_done;
+
+ struct snd_soc_codec *codec;
};
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
int jd_scthr, int jd_thr,
+ int micbias1_dly, int micbias2_dly,
int micbias1_lvl, int micbias2_lvl);
extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
#include <asm/dma.h>
#include <asm/mach-types.h>
-#include <mach/asp.h>
-#include <mach/edma.h>
-#include <mach/mux.h>
-
#include "davinci-pcm.h"
#include "davinci-i2s.h"
#include "davinci-mcasp.h"
.cpu_dai_name = "davinci-mcbsp",
.codec_dai_name = "tlv320aic3x-hifi",
.codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcbsp",
.init = evm_aic3x_init,
.ops = &evm_ops,
};
.cpu_dai_name = "davinci-mcbsp.1",
.codec_dai_name = "tlv320aic3x-hifi",
.codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcbsp.1",
.init = evm_aic3x_init,
.ops = &evm_ops,
};
.init = evm_aic3x_init,
.codec_name = "tlv320aic3x-codec.1-0018",
.ops = &evm_ops,
+ .platform_name = "davinci-mcbsp",
#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
.name = "Voice Codec - CQ93VC",
.stream_name = "CQ93",
.cpu_dai_name = "davinci-vcif",
.codec_dai_name = "cq93vc-hifi",
.codec_name = "cq93vc-codec",
+ .platform_name = "davinci-vcif",
#endif
- .platform_name = "davinci-pcm-audio",
};
static struct snd_soc_dai_link dm6467_evm_dai[] = {
.stream_name = "AIC3X",
.cpu_dai_name= "davinci-mcasp.0",
.codec_dai_name = "tlv320aic3x-hifi",
- .platform_name ="davinci-pcm-audio",
+ .platform_name = "davinci-mcasp.0",
.codec_name = "tlv320aic3x-codec.0-001a",
.init = evm_aic3x_init,
.ops = &evm_ops,
.cpu_dai_name= "davinci-mcasp.1",
.codec_dai_name = "dit-hifi",
.codec_name = "spdif_dit",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcasp.1",
.ops = &evm_spdif_ops,
},
};
.cpu_dai_name = "davinci-mcasp.1",
.codec_dai_name = "tlv320aic3x-hifi",
.codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcasp.1",
.init = evm_aic3x_init,
.ops = &evm_ops,
};
.cpu_dai_name= "davinci-mcasp.0",
.codec_dai_name = "tlv320aic3x-hifi",
.codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcasp.0",
.init = evm_aic3x_init,
.ops = &evm_ops,
};
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/platform_data/davinci_asp.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/asp.h>
-
#include "davinci-pcm.h"
#include "davinci-i2s.h"
if (ret != 0)
goto err_release_clk;
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err_unregister_dai;
+ }
+
return 0;
+err_unregister_dai:
+ snd_soc_unregister_dai(&pdev->dev);
err_release_clk:
clk_disable(dev->clk);
clk_put(dev->clk);
struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev);
+ davinci_soc_platform_unregister(&pdev->dev);
+
clk_disable(dev->clk);
clk_put(dev->clk);
dev->clk = NULL;
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#define DAVINCI_MCASP_WFIFOSTS (0x1014)
#define DAVINCI_MCASP_RFIFOCTL (0x1018)
#define DAVINCI_MCASP_RFIFOSTS (0x101C)
+#define MCASP_VER3_WFIFOCTL (0x1000)
+#define MCASP_VER3_WFIFOSTS (0x1004)
+#define MCASP_VER3_RFIFOCTL (0x1008)
+#define MCASP_VER3_RFIFOSTS (0x100C)
/*
* DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (dev->txnumevt) { /* enable FIFO */
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL,
FIFO_ENABLE);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ mcasp_set_bits(dev->base + MCASP_VER3_WFIFOCTL,
FIFO_ENABLE);
+ break;
+ default:
+ mcasp_clr_bits(dev->base +
+ DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+ mcasp_set_bits(dev->base +
+ DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+ }
}
mcasp_start_tx(dev);
} else {
if (dev->rxnumevt) { /* enable FIFO */
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL,
FIFO_ENABLE);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ mcasp_set_bits(dev->base + MCASP_VER3_RFIFOCTL,
FIFO_ENABLE);
+ break;
+ default:
+ mcasp_clr_bits(dev->base +
+ DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+ mcasp_set_bits(dev->base +
+ DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+ }
}
mcasp_start_rx(dev);
}
static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt) /* disable FIFO */
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ if (dev->txnumevt) { /* disable FIFO */
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL,
FIFO_ENABLE);
+ break;
+ default:
+ mcasp_clr_bits(dev->base +
+ DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+ }
+ }
mcasp_stop_tx(dev);
} else {
- if (dev->rxnumevt) /* disable FIFO */
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ if (dev->rxnumevt) { /* disable FIFO */
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL,
FIFO_ENABLE);
+ break;
+
+ default:
+ mcasp_clr_bits(dev->base +
+ DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+ }
+ }
mcasp_stop_rx(dev);
}
}
if (dev->txnumevt * tx_ser > 64)
dev->txnumevt = 1;
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, tx_ser,
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL, tx_ser,
NUMDMA_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL,
((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
+ break;
+ default:
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ tx_ser, NUMDMA_MASK);
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
+ }
}
if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
if (dev->rxnumevt * rx_ser > 64)
dev->rxnumevt = 1;
-
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, rx_ser,
+ switch (dev->version) {
+ case MCASP_VERSION_3:
+ mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL, rx_ser,
NUMDMA_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL,
((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
+ break;
+ default:
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ rx_ser, NUMDMA_MASK);
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
+ }
}
}
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!dev->clk_active) {
- clk_enable(dev->clk);
- dev->clk_active = 1;
- }
+ ret = pm_runtime_get_sync(dev->dev);
+ if (IS_ERR_VALUE(ret))
+ dev_err(dev->dev, "pm_runtime_get_sync() failed\n");
davinci_mcasp_start(dev, substream->stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
davinci_mcasp_stop(dev, substream->stream);
- if (dev->clk_active) {
- clk_disable(dev->clk);
- dev->clk_active = 0;
- }
-
+ ret = pm_runtime_put_sync(dev->dev);
+ if (IS_ERR_VALUE(ret))
+ dev_err(dev->dev, "pm_runtime_put_sync() failed\n");
break;
case SNDRV_PCM_TRIGGER_STOP:
};
+static const struct of_device_id mcasp_dt_ids[] = {
+ {
+ .compatible = "ti,dm646x-mcasp-audio",
+ .data = (void *)MCASP_VERSION_1,
+ },
+ {
+ .compatible = "ti,da830-mcasp-audio",
+ .data = (void *)MCASP_VERSION_2,
+ },
+ {
+ .compatible = "ti,omap2-mcasp-audio",
+ .data = (void *)MCASP_VERSION_3,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mcasp_dt_ids);
+
+static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_platform_data *pdata = NULL;
+ const struct of_device_id *match =
+ of_match_device(of_match_ptr(mcasp_dt_ids), &pdev->dev);
+
+ const u32 *of_serial_dir32;
+ u8 *of_serial_dir;
+ u32 val;
+ int i, ret = 0;
+
+ if (pdev->dev.platform_data) {
+ pdata = pdev->dev.platform_data;
+ return pdata;
+ } else if (match) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto nodata;
+ }
+ } else {
+ /* control shouldn't reach here. something is wrong */
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ if (match->data)
+ pdata->version = (u8)((int)match->data);
+
+ ret = of_property_read_u32(np, "op-mode", &val);
+ if (ret >= 0)
+ pdata->op_mode = val;
+
+ ret = of_property_read_u32(np, "tdm-slots", &val);
+ if (ret >= 0)
+ pdata->tdm_slots = val;
+
+ ret = of_property_read_u32(np, "num-serializer", &val);
+ if (ret >= 0)
+ pdata->num_serializer = val;
+
+ of_serial_dir32 = of_get_property(np, "serial-dir", &val);
+ val /= sizeof(u32);
+ if (val != pdata->num_serializer) {
+ dev_err(&pdev->dev,
+ "num-serializer(%d) != serial-dir size(%d)\n",
+ pdata->num_serializer, val);
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ if (of_serial_dir32) {
+ of_serial_dir = devm_kzalloc(&pdev->dev,
+ (sizeof(*of_serial_dir) * val),
+ GFP_KERNEL);
+ if (!of_serial_dir) {
+ ret = -ENOMEM;
+ goto nodata;
+ }
+
+ for (i = 0; i < pdata->num_serializer; i++)
+ of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]);
+
+ pdata->serial_dir = of_serial_dir;
+ }
+
+ ret = of_property_read_u32(np, "tx-num-evt", &val);
+ if (ret >= 0)
+ pdata->txnumevt = val;
+
+ ret = of_property_read_u32(np, "rx-num-evt", &val);
+ if (ret >= 0)
+ pdata->rxnumevt = val;
+
+ ret = of_property_read_u32(np, "sram-size-playback", &val);
+ if (ret >= 0)
+ pdata->sram_size_playback = val;
+
+ ret = of_property_read_u32(np, "sram-size-capture", &val);
+ if (ret >= 0)
+ pdata->sram_size_capture = val;
+
+ return pdata;
+
+nodata:
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error populating platform data, err %d\n",
+ ret);
+ pdata = NULL;
+ }
+ return pdata;
+}
+
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct davinci_pcm_dma_params *dma_data;
struct davinci_audio_dev *dev;
int ret;
+ if (!pdev->dev.platform_data && !pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_audio_dev),
GFP_KERNEL);
if (!dev)
return -ENOMEM;
+ pdata = davinci_mcasp_set_pdata_from_of(pdev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -EBUSY;
}
- pdata = pdev->dev.platform_data;
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk))
- return -ENODEV;
+ pm_runtime_enable(&pdev->dev);
- clk_enable(dev->clk);
- dev->clk_active = 1;
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+ return ret;
+ }
dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
if (!dev->base) {
dev->version = pdata->version;
dev->txnumevt = pdata->txnumevt;
dev->rxnumevt = pdata->rxnumevt;
+ dev->dev = &pdev->dev;
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q;
if (ret != 0)
goto err_release_clk;
+
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err_unregister_dai;
+ }
+
return 0;
+err_unregister_dai:
+ snd_soc_unregister_dai(&pdev->dev);
err_release_clk:
- clk_disable(dev->clk);
- clk_put(dev->clk);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return ret;
}
static int davinci_mcasp_remove(struct platform_device *pdev)
{
- struct davinci_audio_dev *dev = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev);
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
+ davinci_soc_platform_unregister(&pdev->dev);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
.driver = {
.name = "davinci-mcasp",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mcasp_dt_ids),
},
};
#define DAVINCI_MCASP_H
#include <linux/io.h>
-#include <mach/asp.h>
+#include <linux/platform_data/davinci_asp.h>
+
#include "davinci-pcm.h"
#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_96000
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
int sample_rate;
- struct clk *clk;
+ struct device *dev;
unsigned int codec_fmt;
- u8 clk_active;
/* McASP specific data */
int tdm_slots;
#include <sound/soc.h>
#include <asm/dma.h>
-#include <mach/edma.h>
#include <mach/sram.h>
#include "davinci-pcm.h"
.pcm_free = davinci_pcm_free,
};
-static int __devinit davinci_soc_platform_probe(struct platform_device *pdev)
+int davinci_soc_platform_register(struct device *dev)
{
- return snd_soc_register_platform(&pdev->dev, &davinci_soc_platform);
+ return snd_soc_register_platform(dev, &davinci_soc_platform);
}
+EXPORT_SYMBOL_GPL(davinci_soc_platform_register);
-static int __devexit davinci_soc_platform_remove(struct platform_device *pdev)
+void davinci_soc_platform_unregister(struct device *dev)
{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
+ snd_soc_unregister_platform(dev);
}
-
-static struct platform_driver davinci_pcm_driver = {
- .driver = {
- .name = "davinci-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = davinci_soc_platform_probe,
- .remove = __devexit_p(davinci_soc_platform_remove),
-};
-
-module_platform_driver(davinci_pcm_driver);
+EXPORT_SYMBOL_GPL(davinci_soc_platform_unregister);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
#ifndef _DAVINCI_PCM_H
#define _DAVINCI_PCM_H
+#include <linux/platform_data/davinci_asp.h>
#include <mach/edma.h>
-#include <mach/asp.h>
-
struct davinci_pcm_dma_params {
int channel; /* sync dma channel ID */
unsigned int fifo_level;
};
+int davinci_soc_platform_register(struct device *dev);
+void davinci_soc_platform_unregister(struct device *dev);
+
#endif
.cpu_dai_name = "davinci-mcbsp",
.codec_dai_name = "pcm3008-hifi",
.codec_name = "pcm3008-codec",
- .platform_name = "davinci-pcm-audio",
+ .platform_name = "davinci-mcbsp",
.ops = &sffsdr_ops,
};
return ret;
}
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ snd_soc_unregister_dai(&pdev->dev);
+ return ret;
+ }
+
return 0;
}
static int davinci_vcif_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
+ davinci_soc_platform_unregister(&pdev->dev);
return 0;
}
+++ /dev/null
-config SND_EP93XX_SOC
- tristate "SoC Audio support for the Cirrus Logic EP93xx series"
- depends on ARCH_EP93XX && SND_SOC
- select SND_SOC_DMAENGINE_PCM
- help
- Say Y or M if you want to add support for codecs attached to
- the EP93xx I2S or AC97 interfaces.
-
-config SND_EP93XX_SOC_I2S
- tristate
-
-config SND_EP93XX_SOC_AC97
- tristate
- select AC97_BUS
- select SND_SOC_AC97_BUS
-
-config SND_EP93XX_SOC_SNAPPERCL15
- tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
- depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
- select SND_EP93XX_SOC_I2S
- select SND_SOC_TLV320AIC23
- help
- Say Y or M here if you want to add support for I2S audio on the
- Bluewater Systems Snapper CL15 module.
-
-config SND_EP93XX_SOC_SIMONE
- tristate "SoC Audio support for Simplemachines Sim.One board"
- depends on SND_EP93XX_SOC && MACH_SIM_ONE
- select SND_EP93XX_SOC_AC97
- select SND_SOC_AC97_CODEC
- help
- Say Y or M here if you want to add support for AC97 audio on the
- Simplemachines Sim.One board.
-
-config SND_EP93XX_SOC_EDB93XX
- tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
- depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
- select SND_EP93XX_SOC_I2S
- select SND_SOC_CS4271
- help
- Say Y or M here if you want to add support for I2S audio on the
- Cirrus Logic EDB93xx boards.
+++ /dev/null
-# EP93xx Platform Support
-snd-soc-ep93xx-objs := ep93xx-pcm.o
-snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
-snd-soc-ep93xx-ac97-objs := ep93xx-ac97.o
-
-obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
-obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
-obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o
-
-# EP93XX Machine Support
-snd-soc-snappercl15-objs := snappercl15.o
-snd-soc-simone-objs := simone.o
-snd-soc-edb93xx-objs := edb93xx.o
-
-obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o
-obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o
-obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o
+++ /dev/null
-/*
- * SoC audio for EDB93xx
- *
- * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver support CS4271 codec being master or slave, working
- * in control port mode, connected either via SPI or I2C.
- * The data format accepted is I2S or left-justified.
- * DAPM support not implemented.
- */
-
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include "ep93xx-pcm.h"
-
-static int edb93xx_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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int err;
- unsigned int mclk_rate;
- unsigned int rate = params_rate(params);
-
- /*
- * According to CS4271 datasheet we use MCLK/LRCK=256 for
- * rates below 50kHz and 128 for higher sample rates
- */
- if (rate < 50000)
- mclk_rate = rate * 64 * 4;
- else
- mclk_rate = rate * 64 * 2;
-
- err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
- SND_SOC_CLOCK_IN);
- if (err)
- return err;
-
- return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
- SND_SOC_CLOCK_OUT);
-}
-
-static struct snd_soc_ops edb93xx_ops = {
- .hw_params = edb93xx_hw_params,
-};
-
-static struct snd_soc_dai_link edb93xx_dai = {
- .name = "CS4271",
- .stream_name = "CS4271 HiFi",
- .platform_name = "ep93xx-pcm-audio",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_name = "spi0.0",
- .codec_dai_name = "cs4271-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &edb93xx_ops,
-};
-
-static struct snd_soc_card snd_soc_edb93xx = {
- .name = "EDB93XX",
- .owner = THIS_MODULE,
- .dai_link = &edb93xx_dai,
- .num_links = 1,
-};
-
-static int __devinit edb93xx_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_edb93xx;
- int ret;
-
- ret = ep93xx_i2s_acquire();
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- ep93xx_i2s_release();
- }
-
- return ret;
-}
-
-static int __devexit edb93xx_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver edb93xx_driver = {
- .driver = {
- .name = "edb93xx-audio",
- .owner = THIS_MODULE,
- },
- .probe = edb93xx_probe,
- .remove = __devexit_p(edb93xx_remove),
-};
-
-module_platform_driver(edb93xx_driver);
-
-MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
-MODULE_DESCRIPTION("ALSA SoC EDB93xx");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:edb93xx-audio");
+++ /dev/null
-/*
- * ASoC driver for Cirrus Logic EP93xx AC97 controller.
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on s3c-ac97 ASoC driver by Jaswinder Singh.
- *
- * 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/delay.h>
-#include <linux/io.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/ac97_codec.h>
-#include <sound/soc.h>
-
-#include <linux/platform_data/dma-ep93xx.h>
-#include "ep93xx-pcm.h"
-
-/*
- * Per channel (1-4) registers.
- */
-#define AC97CH(n) (((n) - 1) * 0x20)
-
-#define AC97DR(n) (AC97CH(n) + 0x0000)
-
-#define AC97RXCR(n) (AC97CH(n) + 0x0004)
-#define AC97RXCR_REN BIT(0)
-#define AC97RXCR_RX3 BIT(3)
-#define AC97RXCR_RX4 BIT(4)
-#define AC97RXCR_CM BIT(15)
-
-#define AC97TXCR(n) (AC97CH(n) + 0x0008)
-#define AC97TXCR_TEN BIT(0)
-#define AC97TXCR_TX3 BIT(3)
-#define AC97TXCR_TX4 BIT(4)
-#define AC97TXCR_CM BIT(15)
-
-#define AC97SR(n) (AC97CH(n) + 0x000c)
-#define AC97SR_TXFE BIT(1)
-#define AC97SR_TXUE BIT(6)
-
-#define AC97RISR(n) (AC97CH(n) + 0x0010)
-#define AC97ISR(n) (AC97CH(n) + 0x0014)
-#define AC97IE(n) (AC97CH(n) + 0x0018)
-
-/*
- * Global AC97 controller registers.
- */
-#define AC97S1DATA 0x0080
-#define AC97S2DATA 0x0084
-#define AC97S12DATA 0x0088
-
-#define AC97RGIS 0x008c
-#define AC97GIS 0x0090
-#define AC97IM 0x0094
-/*
- * Common bits for RGIS, GIS and IM registers.
- */
-#define AC97_SLOT2RXVALID BIT(1)
-#define AC97_CODECREADY BIT(5)
-#define AC97_SLOT2TXCOMPLETE BIT(6)
-
-#define AC97EOI 0x0098
-#define AC97EOI_WINT BIT(0)
-#define AC97EOI_CODECREADY BIT(1)
-
-#define AC97GCR 0x009c
-#define AC97GCR_AC97IFE BIT(0)
-
-#define AC97RESET 0x00a0
-#define AC97RESET_TIMEDRESET BIT(0)
-
-#define AC97SYNC 0x00a4
-#define AC97SYNC_TIMEDSYNC BIT(0)
-
-#define AC97_TIMEOUT msecs_to_jiffies(5)
-
-/**
- * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
- * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
- * @dev: pointer to the platform device dev structure
- * @regs: mapped AC97 controller registers
- * @done: bus ops wait here for an interrupt
- */
-struct ep93xx_ac97_info {
- struct mutex lock;
- struct device *dev;
- void __iomem *regs;
- struct completion done;
-};
-
-/* currently ALSA only supports a single AC97 device */
-static struct ep93xx_ac97_info *ep93xx_ac97_info;
-
-static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_out = {
- .name = "ac97-pcm-out",
- .dma_port = EP93XX_DMA_AAC1,
-};
-
-static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_in = {
- .name = "ac97-pcm-in",
- .dma_port = EP93XX_DMA_AAC1,
-};
-
-static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
- unsigned reg)
-{
- return __raw_readl(info->regs + reg);
-}
-
-static inline void ep93xx_ac97_write_reg(struct ep93xx_ac97_info *info,
- unsigned reg, unsigned val)
-{
- __raw_writel(val, info->regs + reg);
-}
-
-static unsigned short ep93xx_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
- unsigned short val;
-
- mutex_lock(&info->lock);
-
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2RXVALID);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT)) {
- dev_warn(info->dev, "timeout reading register %x\n", reg);
- mutex_unlock(&info->lock);
- return -ETIMEDOUT;
- }
- val = (unsigned short)ep93xx_ac97_read_reg(info, AC97S2DATA);
-
- mutex_unlock(&info->lock);
- return val;
-}
-
-static void ep93xx_ac97_write(struct snd_ac97 *ac97,
- unsigned short reg,
- unsigned short val)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * Writes to the codec need to be done so that slot 2 is filled in
- * before slot 1.
- */
- ep93xx_ac97_write_reg(info, AC97S2DATA, val);
- ep93xx_ac97_write_reg(info, AC97S1DATA, reg);
-
- ep93xx_ac97_write_reg(info, AC97IM, AC97_SLOT2TXCOMPLETE);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "timeout writing register %x\n", reg);
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * We are assuming that before this functions gets called, the codec
- * BIT_CLK is stopped by forcing the codec into powerdown mode. We can
- * control the SYNC signal directly via AC97SYNC register. Using
- * TIMEDSYNC the controller will keep the SYNC high > 1us.
- */
- ep93xx_ac97_write_reg(info, AC97SYNC, AC97SYNC_TIMEDSYNC);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec warm reset timeout\n");
-
- mutex_unlock(&info->lock);
-}
-
-static void ep93xx_ac97_cold_reset(struct snd_ac97 *ac97)
-{
- struct ep93xx_ac97_info *info = ep93xx_ac97_info;
-
- mutex_lock(&info->lock);
-
- /*
- * For doing cold reset, we disable the AC97 controller interface, clear
- * WINT and CODECREADY bits, and finally enable the interface again.
- */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
- ep93xx_ac97_write_reg(info, AC97EOI, AC97EOI_CODECREADY | AC97EOI_WINT);
- ep93xx_ac97_write_reg(info, AC97GCR, AC97GCR_AC97IFE);
-
- /*
- * Now, assert the reset and wait for the codec to become ready.
- */
- ep93xx_ac97_write_reg(info, AC97RESET, AC97RESET_TIMEDRESET);
- ep93xx_ac97_write_reg(info, AC97IM, AC97_CODECREADY);
- if (!wait_for_completion_timeout(&info->done, AC97_TIMEOUT))
- dev_warn(info->dev, "codec cold reset timeout\n");
-
- /*
- * Give the codec some time to come fully out from the reset. This way
- * we ensure that the subsequent reads/writes will work.
- */
- usleep_range(15000, 20000);
-
- mutex_unlock(&info->lock);
-}
-
-static irqreturn_t ep93xx_ac97_interrupt(int irq, void *dev_id)
-{
- struct ep93xx_ac97_info *info = dev_id;
- unsigned status, mask;
-
- /*
- * Just mask out the interrupt and wake up the waiting thread.
- * Interrupts are cleared via reading/writing to slot 1 & 2 registers by
- * the waiting thread.
- */
- status = ep93xx_ac97_read_reg(info, AC97GIS);
- mask = ep93xx_ac97_read_reg(info, AC97IM);
- mask &= ~status;
- ep93xx_ac97_write_reg(info, AC97IM, mask);
-
- complete(&info->done);
- return IRQ_HANDLED;
-}
-
-struct snd_ac97_bus_ops soc_ac97_ops = {
- .read = ep93xx_ac97_read,
- .write = ep93xx_ac97_write,
- .reset = ep93xx_ac97_cold_reset,
- .warm_reset = ep93xx_ac97_warm_reset,
-};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
- unsigned v = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /*
- * Enable compact mode, TX slots 3 & 4, and the TX FIFO
- * itself.
- */
- v |= AC97TXCR_CM;
- v |= AC97TXCR_TX3 | AC97TXCR_TX4;
- v |= AC97TXCR_TEN;
- ep93xx_ac97_write_reg(info, AC97TXCR(1), v);
- } else {
- /*
- * Enable compact mode, RX slots 3 & 4, and the RX FIFO
- * itself.
- */
- v |= AC97RXCR_CM;
- v |= AC97RXCR_RX3 | AC97RXCR_RX4;
- v |= AC97RXCR_REN;
- ep93xx_ac97_write_reg(info, AC97RXCR(1), v);
- }
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /*
- * As per Cirrus EP93xx errata described below:
- *
- * http://www.cirrus.com/en/pubs/errata/ER667E2B.pdf
- *
- * we will wait for the TX FIFO to be empty before
- * clearing the TEN bit.
- */
- unsigned long timeout = jiffies + AC97_TIMEOUT;
-
- do {
- v = ep93xx_ac97_read_reg(info, AC97SR(1));
- if (time_after(jiffies, timeout)) {
- dev_warn(info->dev, "TX timeout\n");
- break;
- }
- } while (!(v & (AC97SR_TXFE | AC97SR_TXUE)));
-
- /* disable the TX FIFO */
- ep93xx_ac97_write_reg(info, AC97TXCR(1), 0);
- } else {
- /* disable the RX FIFO */
- ep93xx_ac97_write_reg(info, AC97RXCR(1), 0);
- }
- break;
-
- default:
- dev_warn(info->dev, "unknown command %d\n", cmd);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int ep93xx_ac97_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ep93xx_pcm_dma_params *dma_data;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = &ep93xx_ac97_pcm_out;
- else
- dma_data = &ep93xx_ac97_pcm_in;
-
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
- return 0;
-}
-
-static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
- .startup = ep93xx_ac97_startup,
- .trigger = ep93xx_ac97_trigger,
-};
-
-static struct snd_soc_dai_driver ep93xx_ac97_dai = {
- .name = "ep93xx-ac97",
- .id = 0,
- .ac97_control = 1,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &ep93xx_ac97_dai_ops,
-};
-
-static int __devinit ep93xx_ac97_probe(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info;
- struct resource *res;
- unsigned int irq;
- int ret;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- info->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (!info->regs)
- return -ENXIO;
-
- irq = platform_get_irq(pdev, 0);
- if (!irq)
- return -ENODEV;
-
- ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
- IRQF_TRIGGER_HIGH, pdev->name, info);
- if (ret)
- goto fail;
-
- dev_set_drvdata(&pdev->dev, info);
-
- mutex_init(&info->lock);
- init_completion(&info->done);
- info->dev = &pdev->dev;
-
- ep93xx_ac97_info = info;
- platform_set_drvdata(pdev, info);
-
- ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
- if (ret)
- goto fail;
-
- return 0;
-
-fail:
- platform_set_drvdata(pdev, NULL);
- ep93xx_ac97_info = NULL;
- dev_set_drvdata(&pdev->dev, NULL);
- return ret;
-}
-
-static int __devexit ep93xx_ac97_remove(struct platform_device *pdev)
-{
- struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
-
- snd_soc_unregister_dai(&pdev->dev);
-
- /* disable the AC97 controller */
- ep93xx_ac97_write_reg(info, AC97GCR, 0);
-
- platform_set_drvdata(pdev, NULL);
- ep93xx_ac97_info = NULL;
- dev_set_drvdata(&pdev->dev, NULL);
-
- return 0;
-}
-
-static struct platform_driver ep93xx_ac97_driver = {
- .probe = ep93xx_ac97_probe,
- .remove = __devexit_p(ep93xx_ac97_remove),
- .driver = {
- .name = "ep93xx-ac97",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(ep93xx_ac97_driver);
-
-MODULE_DESCRIPTION("EP93xx AC97 ASoC Driver");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-ac97");
+++ /dev/null
-/*
- * linux/sound/soc/ep93xx-i2s.c
- * EP93xx I2S driver
- *
- * Copyright (C) 2010 Ryan Mallon
- *
- * Based on the original driver by:
- * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
- * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
- *
- * 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/init.h>
-#include <linux/slab.h>
-#include <linux/clk.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 <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
-#include <linux/platform_data/dma-ep93xx.h>
-
-#include "ep93xx-pcm.h"
-
-#define EP93XX_I2S_TXCLKCFG 0x00
-#define EP93XX_I2S_RXCLKCFG 0x04
-#define EP93XX_I2S_GLCTRL 0x0C
-
-#define EP93XX_I2S_TXLINCTRLDATA 0x28
-#define EP93XX_I2S_TXCTRL 0x2C
-#define EP93XX_I2S_TXWRDLEN 0x30
-#define EP93XX_I2S_TX0EN 0x34
-
-#define EP93XX_I2S_RXLINCTRLDATA 0x58
-#define EP93XX_I2S_RXCTRL 0x5C
-#define EP93XX_I2S_RXWRDLEN 0x60
-#define EP93XX_I2S_RX0EN 0x64
-
-#define EP93XX_I2S_WRDLEN_16 (0 << 0)
-#define EP93XX_I2S_WRDLEN_24 (1 << 0)
-#define EP93XX_I2S_WRDLEN_32 (2 << 0)
-
-#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
-
-#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
-#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
-#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */
-#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
-#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
-
-struct ep93xx_i2s_info {
- struct clk *mclk;
- struct clk *sclk;
- struct clk *lrclk;
- struct ep93xx_pcm_dma_params *dma_params;
- void __iomem *regs;
-};
-
-struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = {
- .name = "i2s-pcm-out",
- .dma_port = EP93XX_DMA_I2S1,
- },
- [SNDRV_PCM_STREAM_CAPTURE] = {
- .name = "i2s-pcm-in",
- .dma_port = EP93XX_DMA_I2S1,
- },
-};
-
-static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
- unsigned reg, unsigned val)
-{
- __raw_writel(val, info->regs + reg);
-}
-
-static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
- unsigned reg)
-{
- return __raw_readl(info->regs + reg);
-}
-
-static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
-{
- unsigned base_reg;
- int i;
-
- if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
- (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
- /* Enable clocks */
- clk_enable(info->mclk);
- clk_enable(info->sclk);
- clk_enable(info->lrclk);
-
- /* Enable i2s */
- ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
- }
-
- /* Enable fifos */
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- base_reg = EP93XX_I2S_TX0EN;
- else
- base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
-}
-
-static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
-{
- unsigned base_reg;
- int i;
-
- /* Disable fifos */
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- base_reg = EP93XX_I2S_TX0EN;
- else
- base_reg = EP93XX_I2S_RX0EN;
- for (i = 0; i < 3; i++)
- ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
-
- if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
- (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
- /* Disable i2s */
- ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
-
- /* Disable clocks */
- clk_disable(info->lrclk);
- clk_disable(info->sclk);
- clk_disable(info->mclk);
- }
-}
-
-static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_dma_data(cpu_dai, substream,
- &info->dma_params[substream->stream]);
- return 0;
-}
-
-static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
-
- ep93xx_i2s_disable(info, substream->stream);
-}
-
-static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
- unsigned int clk_cfg, lin_ctrl;
-
- clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
- lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- clk_cfg |= EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
- break;
-
- case SND_SOC_DAIFMT_LEFT_J:
- clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
- break;
-
- case SND_SOC_DAIFMT_RIGHT_J:
- clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
- break;
-
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* CPU is master */
- clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
- break;
-
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Codec is master */
- clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
- break;
-
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- /* Negative bit clock, lrclk low on left word */
- clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
- break;
-
- case SND_SOC_DAIFMT_NB_IF:
- /* Negative bit clock, lrclk low on right word */
- clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
- clk_cfg |= EP93XX_I2S_CLKCFG_REL;
- break;
-
- case SND_SOC_DAIFMT_IB_NF:
- /* Positive bit clock, lrclk low on left word */
- clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
- clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
- break;
-
- case SND_SOC_DAIFMT_IB_IF:
- /* Positive bit clock, lrclk low on right word */
- clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
- break;
- }
-
- /* Write new register values */
- ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
- ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
- return 0;
-}
-
-static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- unsigned word_len, div, sdiv, lrdiv;
- int err;
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- word_len = EP93XX_I2S_WRDLEN_16;
- break;
-
- case SNDRV_PCM_FORMAT_S24_LE:
- word_len = EP93XX_I2S_WRDLEN_24;
- break;
-
- case SNDRV_PCM_FORMAT_S32_LE:
- word_len = EP93XX_I2S_WRDLEN_32;
- break;
-
- default:
- return -EINVAL;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
- else
- ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
-
- /*
- * EP93xx I2S module can be setup so SCLK / LRCLK value can be
- * 32, 64, 128. MCLK / SCLK value can be 2 and 4.
- * We set LRCLK equal to `rate' and minimum SCLK / LRCLK
- * value is 64, because our sample size is 32 bit * 2 channels.
- * I2S standard permits us to transmit more bits than
- * the codec uses.
- */
- div = clk_get_rate(info->mclk) / params_rate(params);
- sdiv = 4;
- if (div > (256 + 512) / 2) {
- lrdiv = 128;
- } else {
- lrdiv = 64;
- if (div < (128 + 256) / 2)
- sdiv = 2;
- }
-
- err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
- if (err)
- return err;
-
- err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
- if (err)
- return err;
-
- ep93xx_i2s_enable(info, substream->stream);
- return 0;
-}
-
-static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai);
-
- if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
- return -EINVAL;
-
- return clk_set_rate(info->mclk, freq);
-}
-
-#ifdef CONFIG_PM
-static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
-
- if (!dai->active)
- return 0;
-
- ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
- ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
-
- return 0;
-}
-
-static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
-{
- struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
-
- if (!dai->active)
- return 0;
-
- ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
- ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
-
- return 0;
-}
-#else
-#define ep93xx_i2s_suspend NULL
-#define ep93xx_i2s_resume NULL
-#endif
-
-static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
- .startup = ep93xx_i2s_startup,
- .shutdown = ep93xx_i2s_shutdown,
- .hw_params = ep93xx_i2s_hw_params,
- .set_sysclk = ep93xx_i2s_set_sysclk,
- .set_fmt = ep93xx_i2s_set_dai_fmt,
-};
-
-#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
-
-static struct snd_soc_dai_driver ep93xx_i2s_dai = {
- .symmetric_rates= 1,
- .suspend = ep93xx_i2s_suspend,
- .resume = ep93xx_i2s_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = EP93XX_I2S_FORMATS,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = EP93XX_I2S_FORMATS,
- },
- .ops = &ep93xx_i2s_dai_ops,
-};
-
-static int ep93xx_i2s_probe(struct platform_device *pdev)
-{
- struct ep93xx_i2s_info *info;
- struct resource *res;
- int err;
-
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- info->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (!info->regs)
- return -ENXIO;
-
- info->mclk = clk_get(&pdev->dev, "mclk");
- if (IS_ERR(info->mclk)) {
- err = PTR_ERR(info->mclk);
- goto fail;
- }
-
- info->sclk = clk_get(&pdev->dev, "sclk");
- if (IS_ERR(info->sclk)) {
- err = PTR_ERR(info->sclk);
- goto fail_put_mclk;
- }
-
- info->lrclk = clk_get(&pdev->dev, "lrclk");
- if (IS_ERR(info->lrclk)) {
- err = PTR_ERR(info->lrclk);
- goto fail_put_sclk;
- }
-
- dev_set_drvdata(&pdev->dev, info);
- info->dma_params = ep93xx_i2s_dma_params;
-
- err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
- if (err)
- goto fail_put_lrclk;
-
- return 0;
-
-fail_put_lrclk:
- dev_set_drvdata(&pdev->dev, NULL);
- clk_put(info->lrclk);
-fail_put_sclk:
- clk_put(info->sclk);
-fail_put_mclk:
- clk_put(info->mclk);
-fail:
- return err;
-}
-
-static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
-{
- struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
-
- snd_soc_unregister_dai(&pdev->dev);
- dev_set_drvdata(&pdev->dev, NULL);
- clk_put(info->lrclk);
- clk_put(info->sclk);
- clk_put(info->mclk);
- return 0;
-}
-
-static struct platform_driver ep93xx_i2s_driver = {
- .probe = ep93xx_i2s_probe,
- .remove = __devexit_p(ep93xx_i2s_remove),
- .driver = {
- .name = "ep93xx-i2s",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(ep93xx_i2s_driver);
-
-MODULE_ALIAS("platform:ep93xx-i2s");
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("EP93XX I2S driver");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
- *
- * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
- * Copyright (C) 2006 Applied Data Systems
- *
- * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
- * Copyright (c) 2008 Ryan Mallon
- *
- * 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/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include <linux/platform_data/dma-ep93xx.h>
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
-
-#include "ep93xx-pcm.h"
-
-static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER),
-
- .rates = SNDRV_PCM_RATE_8000_192000,
- .rate_min = SNDRV_PCM_RATE_8000,
- .rate_max = SNDRV_PCM_RATE_192000,
-
- .formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE),
-
- .buffer_bytes_max = 131072,
- .period_bytes_min = 32,
- .period_bytes_max = 32768,
- .periods_min = 1,
- .periods_max = 32,
- .fifo_size = 32,
-};
-
-static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
-{
- struct ep93xx_dma_data *data = filter_param;
-
- if (data->direction == ep93xx_dma_chan_direction(chan)) {
- chan->private = data;
- return true;
- }
-
- return false;
-}
-
-static int ep93xx_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 ep93xx_pcm_dma_params *dma_params;
- struct ep93xx_dma_data *dma_data;
- int ret;
-
- snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
-
- dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL);
- if (!dma_data)
- return -ENOMEM;
-
- dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
- dma_data->port = dma_params->dma_port;
- dma_data->name = dma_params->name;
- dma_data->direction = snd_pcm_substream_to_dma_direction(substream);
-
- ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data);
- if (ret) {
- kfree(dma_data);
- return ret;
- }
-
- snd_dmaengine_pcm_set_data(substream, dma_data);
-
- return 0;
-}
-
-static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
-
- snd_dmaengine_pcm_close(substream);
- kfree(dma_data);
- return 0;
-}
-
-static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
- return 0;
-}
-
-static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_set_runtime_buffer(substream, NULL);
- return 0;
-}
-
-static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops ep93xx_pcm_ops = {
- .open = ep93xx_pcm_open,
- .close = ep93xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = ep93xx_pcm_hw_params,
- .hw_free = ep93xx_pcm_hw_free,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer_no_residue,
- .mmap = ep93xx_pcm_mmap,
-};
-
-static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- buf->bytes = size;
-
- return (buf->area == NULL) ? -ENOMEM : 0;
-}
-
-static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
- buf->addr);
- buf->area = NULL;
- }
-}
-
-static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
-
-static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &ep93xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver ep93xx_soc_platform = {
- .ops = &ep93xx_pcm_ops,
- .pcm_new = &ep93xx_pcm_new,
- .pcm_free = &ep93xx_pcm_free_dma_buffers,
-};
-
-static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
-}
-
-static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver ep93xx_pcm_driver = {
- .driver = {
- .name = "ep93xx-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = ep93xx_soc_platform_probe,
- .remove = __devexit_p(ep93xx_soc_platform_remove),
-};
-
-module_platform_driver(ep93xx_pcm_driver);
-
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-pcm-audio");
+++ /dev/null
-/*
- * sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface
- *
- * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
- * Copyright (C) 2006 Applied Data Systems
- *
- * 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 _EP93XX_SND_SOC_PCM_H
-#define _EP93XX_SND_SOC_PCM_H
-
-struct ep93xx_pcm_dma_params {
- char *name;
- int dma_port;
-};
-
-#endif /* _EP93XX_SND_SOC_PCM_H */
+++ /dev/null
-/*
- * simone.c -- ASoC audio for Simplemachines Sim.One board
- *
- * Copyright (c) 2010 Mika Westerberg
- *
- * Based on snappercl15 machine driver by Ryan Mallon.
- *
- * 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/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-#include "ep93xx-pcm.h"
-
-static struct snd_soc_dai_link simone_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "ep93xx-ac97",
- .codec_dai_name = "ac97-hifi",
- .codec_name = "ac97-codec",
- .platform_name = "ep93xx-pcm-audio",
-};
-
-static struct snd_soc_card snd_soc_simone = {
- .name = "Sim.One",
- .owner = THIS_MODULE,
- .dai_link = &simone_dai,
- .num_links = 1,
-};
-
-static struct platform_device *simone_snd_ac97_device;
-
-static int __devinit simone_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_simone;
- int ret;
-
- simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
- -1, NULL, 0);
- if (IS_ERR(simone_snd_ac97_device))
- return PTR_ERR(simone_snd_ac97_device);
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- platform_device_unregister(simone_snd_ac97_device);
- }
-
- return ret;
-}
-
-static int __devexit simone_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- platform_device_unregister(simone_snd_ac97_device);
-
- return 0;
-}
-
-static struct platform_driver simone_driver = {
- .driver = {
- .name = "simone-audio",
- .owner = THIS_MODULE,
- },
- .probe = simone_probe,
- .remove = __devexit_p(simone_remove),
-};
-
-module_platform_driver(simone_driver);
-
-MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:simone-audio");
+++ /dev/null
-/*
- * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
- *
- * Copyright (C) 2008 Bluewater Systems Ltd
- * Author: Ryan Mallon
- *
- * 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/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "ep93xx-pcm.h"
-
-#define CODEC_CLOCK 5644800
-
-static int snappercl15_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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int err;
-
- err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_IN);
- if (err)
- return err;
-
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
- SND_SOC_CLOCK_OUT);
- if (err)
- return err;
-
- return 0;
-}
-
-static struct snd_soc_ops snappercl15_ops = {
- .hw_params = snappercl15_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- {"LLINEIN", NULL, "Line In"},
- {"RLINEIN", NULL, "Line In"},
-
- {"MICIN", NULL, "Mic Jack"},
-};
-
-static int snappercl15_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
- ARRAY_SIZE(tlv320aic23_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
- return 0;
-}
-
-static struct snd_soc_dai_link snappercl15_dai = {
- .name = "tlv320aic23",
- .stream_name = "AIC23",
- .cpu_dai_name = "ep93xx-i2s",
- .codec_dai_name = "tlv320aic23-hifi",
- .codec_name = "tlv320aic23-codec.0-001a",
- .platform_name = "ep93xx-pcm-audio",
- .init = snappercl15_tlv320aic23_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &snappercl15_ops,
-};
-
-static struct snd_soc_card snd_soc_snappercl15 = {
- .name = "Snapper CL15",
- .owner = THIS_MODULE,
- .dai_link = &snappercl15_dai,
- .num_links = 1,
-};
-
-static int __devinit snappercl15_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_snappercl15;
- int ret;
-
- ret = ep93xx_i2s_acquire();
- if (ret)
- return ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
- ret);
- ep93xx_i2s_release();
- }
-
- return ret;
-}
-
-static int __devexit snappercl15_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
- ep93xx_i2s_release();
-
- return 0;
-}
-
-static struct platform_driver snappercl15_driver = {
- .driver = {
- .name = "snappercl15-audio",
- .owner = THIS_MODULE,
- },
- .probe = snappercl15_probe,
- .remove = __devexit_p(snappercl15_remove),
-};
-
-module_platform_driver(snappercl15_driver);
-
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:snappercl15-audio");
menuconfig SND_POWERPC_SOC
tristate "SoC Audio for Freescale PowerPC CPUs"
- depends on FSL_SOC
+ depends on FSL_SOC || PPC_MPC52xx
help
Say Y or M if you want to add support for codecs attached to
the PowerPC CPUs.
.num_links = 1,
};
-static struct platform_device *eukrea_tlv320_snd_device;
-
-static int __init eukrea_tlv320_init(void)
+static int __devinit eukrea_tlv320_probe(struct platform_device *pdev)
{
int ret;
int int_port = 0, ext_port;
return 0;
}
- eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1);
- if (!eukrea_tlv320_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320);
- ret = platform_device_add(eukrea_tlv320_snd_device);
-
- if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- platform_device_put(eukrea_tlv320_snd_device);
- }
+ eukrea_tlv320.dev = &pdev->dev;
+ ret = snd_soc_register_card(&eukrea_tlv320);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
return ret;
}
-static void __exit eukrea_tlv320_exit(void)
+static int __devexit eukrea_tlv320_remove(struct platform_device *pdev)
{
- platform_device_unregister(eukrea_tlv320_snd_device);
+ snd_soc_unregister_card(&eukrea_tlv320);
+
+ return 0;
}
-module_init(eukrea_tlv320_init);
-module_exit(eukrea_tlv320_exit);
+static struct platform_driver eukrea_tlv320_driver = {
+ .driver = {
+ .name = "eukrea_tlv320",
+ .owner = THIS_MODULE,
+ },
+ .probe = eukrea_tlv320_probe,
+ .remove = __devexit_p(eukrea_tlv320_remove),};
+
+module_platform_driver(eukrea_tlv320_driver);
MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");
MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:eukrea_tlv320");
if (dma_private->irq)
free_irq(dma_private->irq, dma_private);
- if (dma_private->ld_buf_phys) {
- dma_unmap_single(dev, dma_private->ld_buf_phys,
- sizeof(dma_private->link),
- DMA_TO_DEVICE);
- }
-
/* Deallocate the fsl_dma_private structure */
dma_free_coherent(dev, sizeof(struct fsl_dma_private),
dma_private, dma_private->ld_buf_phys);
if (!buf)
return -ENOMEM;
- if (!audmux_base)
- return -ENOSYS;
-
if (audmux_clk)
clk_prepare_enable(audmux_clk);
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
+ if (!dma_data)
+ return -ENOMEM;
+
dma_data->peripheral_type = dma_params->shared_peripheral ?
IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI;
dma_data->priority = DMA_PRIO_HIGH;
ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
if (ret) {
kfree(dma_data);
- return 0;
+ return ret;
}
snd_dmaengine_pcm_set_data(substream, dma_data);
static struct snd_soc_dai_driver imx_ssi_dai = {
.probe = imx_ssi_dai_probe,
.playback = {
- /* The SSI does not support monaural audio. */
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
int ret = 0;
struct snd_soc_dai_driver *dai;
- ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
+ ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL);
if (!ssi)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, ssi);
ssi->irq = platform_get_irq(pdev, 0);
- ssi->clk = clk_get(&pdev->dev, NULL);
+ ssi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ssi->clk)) {
ret = PTR_ERR(ssi->clk);
dev_err(&pdev->dev, "Cannot get the clock: %d\n",
goto failed_get_resource;
}
- if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) {
- dev_err(&pdev->dev, "request_mem_region failed\n");
- ret = -EBUSY;
- goto failed_get_resource;
- }
-
- ssi->base = ioremap(res->start, resource_size(res));
+ ssi->base = devm_request_and_ioremap(&pdev->dev, res);
if (!ssi->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENODEV;
- goto failed_ioremap;
+ goto failed_register;
}
if (ssi->flags & IMX_SSI_USE_AC97) {
if (ac97_ssi) {
+ dev_err(&pdev->dev, "AC'97 SSI already registered\n");
ret = -EBUSY;
- goto failed_ac97;
+ goto failed_register;
}
ac97_ssi = ssi;
setup_channel_to_ac97(ssi);
failed_pdev_fiq_alloc:
snd_soc_unregister_dai(&pdev->dev);
failed_register:
-failed_ac97:
- iounmap(ssi->base);
-failed_ioremap:
release_mem_region(res->start, resource_size(res));
failed_get_resource:
clk_disable_unprepare(ssi->clk);
- clk_put(ssi->clk);
failed_clk:
- kfree(ssi);
return ret;
}
if (ssi->flags & IMX_SSI_USE_AC97)
ac97_ssi = NULL;
- iounmap(ssi->base);
release_mem_region(res->start, resource_size(res));
clk_disable_unprepare(ssi->clk);
- clk_put(ssi->clk);
- kfree(ssi);
return 0;
}
.pcm_free = &psc_dma_free,
};
-static int mpc5200_hpcd_probe(struct platform_device *op)
+int mpc5200_audio_dma_create(struct platform_device *op)
{
phys_addr_t fifo;
struct psc_dma *psc_dma;
iounmap(regs);
return ret;
}
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);
-static int mpc5200_hpcd_remove(struct platform_device *op)
+int mpc5200_audio_dma_destroy(struct platform_device *op)
{
struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);
return 0;
}
-
-static struct of_device_id mpc5200_hpcd_match[] = {
- { .compatible = "fsl,mpc5200-pcm", },
- {}
-};
-MODULE_DEVICE_TABLE(of, mpc5200_hpcd_match);
-
-static struct platform_driver mpc5200_hpcd_of_driver = {
- .probe = mpc5200_hpcd_probe,
- .remove = mpc5200_hpcd_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "mpc5200-pcm-audio",
- .of_match_table = mpc5200_hpcd_match,
- }
-};
-
-module_platform_driver(mpc5200_hpcd_of_driver);
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy);
MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");
return &psc_dma->playback;
}
+int mpc5200_audio_dma_create(struct platform_device *op);
+int mpc5200_audio_dma_destroy(struct platform_device *op);
+
#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */
static struct snd_soc_dai_driver psc_ac97_dai[] = {
{
+ .name = "mpc5200-psc-ac97.0",
.ac97_control = 1,
.probe = psc_ac97_probe,
.playback = {
+ .stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 6,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S32_BE,
},
.capture = {
+ .stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.ops = &psc_ac97_analog_ops,
},
{
+ .name = "mpc5200-psc-ac97.1",
.ac97_control = 1,
.playback = {
+ .stream_name = "AC97 SPDIF",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 | \
struct snd_ac97 ac97;
struct mpc52xx_psc __iomem *regs;
+ rc = mpc5200_audio_dma_create(op);
+ if (rc != 0)
+ return rc;
+
rc = snd_soc_register_dais(&op->dev, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
if (rc != 0) {
dev_err(&op->dev, "Failed to register DAI\n");
static int __devexit psc_ac97_of_remove(struct platform_device *op)
{
+ mpc5200_audio_dma_destroy(op);
snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_ac97_dai));
return 0;
}
};
static struct snd_soc_dai_driver psc_i2s_dai[] = {{
+ .name = "mpc5200-psc-i2s.0",
.playback = {
+ .stream_name = "I2S Playback",
.channels_min = 2,
.channels_max = 2,
.rates = PSC_I2S_RATES,
.formats = PSC_I2S_FORMATS,
},
.capture = {
+ .stream_name = "I2S Capture",
.channels_min = 2,
.channels_max = 2,
.rates = PSC_I2S_RATES,
struct psc_dma *psc_dma;
struct mpc52xx_psc __iomem *regs;
+ rc = mpc5200_audio_dma_create(op);
+ if (rc != 0)
+ return rc;
+
rc = snd_soc_register_dais(&op->dev, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
if (rc != 0) {
pr_err("Failed to register DAI\n");
static int __devexit psc_i2s_of_remove(struct platform_device *op)
{
+ mpc5200_audio_dma_destroy(op);
snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_i2s_dai));
return 0;
}
container_of(dev, struct platform_device, dev);
struct device_node *np = ssi_pdev->dev.of_node;
struct device_node *codec_np = NULL;
- struct platform_device *sound_device = NULL;
struct mpc8610_hpcd_data *machine_data;
int ret = -ENODEV;
const char *sprop;
machine_data->card.probe = mpc8610_hpcd_machine_probe;
machine_data->card.remove = mpc8610_hpcd_machine_remove;
machine_data->card.name = pdev->name; /* The platform driver name */
+ machine_data->card.owner = THIS_MODULE;
+ machine_data->card.dev = &pdev->dev;
machine_data->card.num_links = 2;
machine_data->card.dai_link = machine_data->dai;
- /* Allocate a new audio platform device structure */
- sound_device = platform_device_alloc("soc-audio", -1);
- if (!sound_device) {
- dev_err(&pdev->dev, "platform device alloc failed\n");
- ret = -ENOMEM;
- goto error;
- }
-
- /* Associate the card data with the sound device */
- platform_set_drvdata(sound_device, &machine_data->card);
-
/* Register with ASoC */
- ret = platform_device_add(sound_device);
+ ret = snd_soc_register_card(&machine_data->card);
if (ret) {
- dev_err(&pdev->dev, "platform device add failed\n");
- goto error_sound;
+ dev_err(&pdev->dev, "could not register card\n");
+ goto error;
}
- dev_set_drvdata(&pdev->dev, sound_device);
of_node_put(codec_np);
return 0;
-error_sound:
- platform_device_put(sound_device);
error:
kfree(machine_data);
error_alloc:
*/
static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev)
{
- struct platform_device *sound_device = dev_get_drvdata(&pdev->dev);
- struct snd_soc_card *card = platform_get_drvdata(sound_device);
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
struct mpc8610_hpcd_data *machine_data =
container_of(card, struct mpc8610_hpcd_data, card);
- platform_device_unregister(sound_device);
-
+ snd_soc_unregister_card(card);
kfree(machine_data);
- sound_device->dev.platform_data = NULL;
-
- dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/platform_data/asoc-mx27vis.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <asm/mach-types.h>
-#include <mach/iomux-mx27.h>
#include "../codecs/tlv320aic32x4.h"
#include "imx-ssi.h"
#define MX27VIS_AMP_GAIN 0
#define MX27VIS_AMP_MUTE 1
-#define MX27VIS_PIN_G0 (GPIO_PORTF + 9)
-#define MX27VIS_PIN_G1 (GPIO_PORTF + 8)
-#define MX27VIS_PIN_SDL (GPIO_PORTE + 5)
-#define MX27VIS_PIN_SDR (GPIO_PORTF + 7)
-
static int mx27vis_amp_gain;
static int mx27vis_amp_mute;
-
-static const int mx27vis_amp_pins[] = {
- MX27VIS_PIN_G0 | GPIO_GPIO | GPIO_OUT,
- MX27VIS_PIN_G1 | GPIO_GPIO | GPIO_OUT,
- MX27VIS_PIN_SDL | GPIO_GPIO | GPIO_OUT,
- MX27VIS_PIN_SDR | GPIO_GPIO | GPIO_OUT,
-};
+static int mx27vis_amp_gain0_gpio;
+static int mx27vis_amp_gain1_gpio;
+static int mx27vis_amp_mutel_gpio;
+static int mx27vis_amp_muter_gpio;
static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
switch (reg) {
case MX27VIS_AMP_GAIN:
- gpio_set_value(MX27VIS_PIN_G0, value & 1);
- gpio_set_value(MX27VIS_PIN_G1, value >> 1);
+ gpio_set_value(mx27vis_amp_gain0_gpio, value & 1);
+ gpio_set_value(mx27vis_amp_gain1_gpio, value >> 1);
mx27vis_amp_gain = value;
break;
case MX27VIS_AMP_MUTE:
- gpio_set_value(MX27VIS_PIN_SDL, value & 1);
- gpio_set_value(MX27VIS_PIN_SDR, value >> 1);
+ gpio_set_value(mx27vis_amp_mutel_gpio, value & 1);
+ gpio_set_value(mx27vis_amp_muter_gpio, value >> 1);
mx27vis_amp_mute = value;
break;
}
static int __devinit mx27vis_aic32x4_probe(struct platform_device *pdev)
{
+ struct snd_mx27vis_platform_data *pdata = pdev->dev.platform_data;
int ret;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ mx27vis_amp_gain0_gpio = pdata->amp_gain0_gpio;
+ mx27vis_amp_gain1_gpio = pdata->amp_gain1_gpio;
+ mx27vis_amp_mutel_gpio = pdata->amp_mutel_gpio;
+ mx27vis_amp_muter_gpio = pdata->amp_muter_gpio;
+
mx27vis_aic32x4.dev = &pdev->dev;
ret = snd_soc_register_card(&mx27vis_aic32x4);
if (ret) {
IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
);
- ret = mxc_gpio_setup_multiple_pins(mx27vis_amp_pins,
- ARRAY_SIZE(mx27vis_amp_pins), "MX27VIS_AMP");
- if (ret)
- printk(KERN_ERR "ASoC: unable to setup gpios\n");
-
return ret;
}
container_of(dev, struct platform_device, dev);
struct device_node *np = ssi_pdev->dev.of_node;
struct device_node *codec_np = NULL;
- struct platform_device *sound_device = NULL;
struct machine_data *mdata;
int ret = -ENODEV;
const char *sprop;
mdata->card.probe = p1022_ds_machine_probe;
mdata->card.remove = p1022_ds_machine_remove;
mdata->card.name = pdev->name; /* The platform driver name */
+ mdata->card.owner = THIS_MODULE;
+ mdata->card.dev = &pdev->dev;
mdata->card.num_links = 2;
mdata->card.dai_link = mdata->dai;
- /* Allocate a new audio platform device structure */
- sound_device = platform_device_alloc("soc-audio", -1);
- if (!sound_device) {
- dev_err(&pdev->dev, "platform device alloc failed\n");
- ret = -ENOMEM;
- goto error;
- }
-
- /* Associate the card data with the sound device */
- platform_set_drvdata(sound_device, &mdata->card);
-
/* Register with ASoC */
- ret = platform_device_add(sound_device);
+ ret = snd_soc_register_card(&mdata->card);
if (ret) {
- dev_err(&pdev->dev, "platform device add failed\n");
+ dev_err(&pdev->dev, "could not register card\n");
goto error;
}
- dev_set_drvdata(&pdev->dev, sound_device);
of_node_put(codec_np);
return 0;
error:
- if (sound_device)
- platform_device_put(sound_device);
-
kfree(mdata);
error_put:
of_node_put(codec_np);
*/
static int __devexit p1022_ds_remove(struct platform_device *pdev)
{
- struct platform_device *sound_device = dev_get_drvdata(&pdev->dev);
- struct snd_soc_card *card = platform_get_drvdata(sound_device);
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
struct machine_data *mdata =
container_of(card, struct machine_data, card);
- platform_device_unregister(sound_device);
-
+ snd_soc_unregister_card(card);
kfree(mdata);
- sound_device->dev.platform_data = NULL;
-
- dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
-#include <linux/dma-mapping.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
#include <sound/soc.h>
#include "mpc5200_dma.h"
-#include "mpc5200_psc_ac97.h"
-#include "../codecs/wm9712.h"
#define DRV_NAME "pcm030-audio-fabric"
+struct pcm030_audio_data {
+ struct snd_soc_card *card;
+ struct platform_device *codec_device;
+};
+
static struct snd_soc_dai_link pcm030_fabric_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 Analog",
.codec_dai_name = "wm9712-hifi",
.cpu_dai_name = "mpc5200-psc-ac97.0",
- .platform_name = "mpc5200-pcm-audio",
.codec_name = "wm9712-codec",
},
{
.stream_name = "AC97 IEC958",
.codec_dai_name = "wm9712-aux",
.cpu_dai_name = "mpc5200-psc-ac97.1",
- .platform_name = "mpc5200-pcm-audio",
.codec_name = "wm9712-codec",
},
};
-static struct snd_soc_card card = {
+static struct snd_soc_card pcm030_card = {
.name = "pcm030",
.owner = THIS_MODULE,
.dai_link = pcm030_fabric_dai,
.num_links = ARRAY_SIZE(pcm030_fabric_dai),
};
-static __init int pcm030_fabric_init(void)
+static int __init pcm030_fabric_probe(struct platform_device *op)
{
- struct platform_device *pdev;
- int rc;
+ struct device_node *np = op->dev.of_node;
+ struct device_node *platform_np;
+ struct snd_soc_card *card = &pcm030_card;
+ struct pcm030_audio_data *pdata;
+ int ret;
+ int i;
if (!of_machine_is_compatible("phytec,pcm030"))
return -ENODEV;
- pdev = platform_device_alloc("soc-audio", 1);
- if (!pdev) {
- pr_err("pcm030_fabric_init: platform_device_alloc() failed\n");
- return -ENODEV;
- }
+ pdata = devm_kzalloc(&op->dev, sizeof(struct pcm030_audio_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ card->dev = &op->dev;
+ platform_set_drvdata(op, pdata);
- platform_set_drvdata(pdev, &card);
+ pdata->card = card;
- rc = platform_device_add(pdev);
- if (rc) {
- pr_err("pcm030_fabric_init: platform_device_add() failed\n");
- platform_device_put(pdev);
+ platform_np = of_parse_phandle(np, "asoc-platform", 0);
+ if (!platform_np) {
+ dev_err(&op->dev, "ac97 not registered\n");
return -ENODEV;
}
- return 0;
+
+ for (i = 0; i < card->num_links; i++)
+ card->dai_link[i].platform_of_node = platform_np;
+
+ ret = request_module("snd-soc-wm9712");
+ if (ret)
+ dev_err(&op->dev, "request_module returned: %d\n", ret);
+
+ pdata->codec_device = platform_device_alloc("wm9712-codec", -1);
+ if (!pdata->codec_device)
+ dev_err(&op->dev, "platform_device_alloc() failed\n");
+
+ ret = platform_device_add(pdata->codec_device);
+ if (ret)
+ dev_err(&op->dev, "platform_device_add() failed: %d\n", ret);
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&op->dev, "snd_soc_register_card() failed: %d\n", ret);
+
+ return ret;
+}
+
+static int __devexit pcm030_fabric_remove(struct platform_device *op)
+{
+ struct pcm030_audio_data *pdata = platform_get_drvdata(op);
+ int ret;
+
+ ret = snd_soc_unregister_card(pdata->card);
+ platform_device_unregister(pdata->codec_device);
+
+ return ret;
}
-module_init(pcm030_fabric_init);
+static struct of_device_id pcm030_audio_match[] = {
+ { .compatible = "phytec,pcm030-audio-fabric", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcm030_audio_match);
+
+static struct platform_driver pcm030_fabric_driver = {
+ .probe = pcm030_fabric_probe,
+ .remove = __devexit_p(pcm030_fabric_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = pcm030_audio_match,
+ },
+};
+
+module_platform_driver(pcm030_fabric_driver);
MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
.platform_name = "sst-platform",
.init = NULL,
},
+ {
+ .name = "Medfield Compress",
+ .stream_name = "Speaker",
+ .cpu_dai_name = "Compress-cpu-dai",
+ .codec_dai_name = "SN95031 Speaker",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
};
/* SoC card */
--- /dev/null
+#ifndef __SST_DSP_H__
+#define __SST_DSP_H__
+/*
+ * sst_dsp.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-12 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+enum sst_codec_types {
+ /* AUDIO/MUSIC CODEC Type Definitions */
+ SST_CODEC_TYPE_UNKNOWN = 0,
+ SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
+ SST_CODEC_TYPE_MP3,
+ SST_CODEC_TYPE_MP24,
+ SST_CODEC_TYPE_AAC,
+ SST_CODEC_TYPE_AACP,
+ SST_CODEC_TYPE_eAACP,
+};
+
+enum stream_type {
+ SST_STREAM_TYPE_NONE = 0,
+ SST_STREAM_TYPE_MUSIC = 1,
+};
+
+struct snd_pcm_params {
+ u16 codec; /* codec type */
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u32 reserved; /* Bitrate in bits per second */
+ u32 sfreq; /* Sampling rate in Hz */
+ u8 use_offload_path;
+ u8 reserved2;
+ u16 reserved3;
+ u8 channel_map[8];
+} __packed;
+
+/* MP3 Music Parameters Message */
+struct snd_mp3_params {
+ u16 codec;
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 crc_check; /* crc_check - disable (0) or enable (1) */
+ u8 reserved1; /* unused*/
+ u16 reserved2; /* Unused */
+} __packed;
+
+#define AAC_BIT_STREAM_ADTS 0
+#define AAC_BIT_STREAM_ADIF 1
+#define AAC_BIT_STREAM_RAW 2
+
+/* AAC Music Parameters Message */
+struct snd_aac_params {
+ u16 codec;
+ u8 num_chan; /* 1=Mono, 2=Stereo*/
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
+ u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */
+ u16 reser2;
+ u32 externalsr; /*sampling rate of basic AAC raw bit stream*/
+ u8 sbr_signalling;/*disable/enable/set automode the SBR tool.AAC+*/
+ u8 reser1;
+ u16 reser3;
+} __packed;
+
+/* WMA Music Parameters Message */
+struct snd_wma_params {
+ u16 codec;
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u32 brate; /* Use the hard coded value. */
+ u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
+ u32 channel_mask; /* Channel Mask */
+ u16 format_tag; /* Format Tag */
+ u16 block_align; /* packet size */
+ u16 wma_encode_opt;/* Encoder option */
+ u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */
+ u8 reserved; /* reserved */
+} __packed;
+
+/* Codec params struture */
+union snd_sst_codec_params {
+ struct snd_pcm_params pcm_params;
+ struct snd_mp3_params mp3_params;
+ struct snd_aac_params aac_params;
+ struct snd_wma_params wma_params;
+} __packed;
+
+/* Address and size info of a frame buffer */
+struct sst_address_info {
+ u32 addr; /* Address at IA */
+ u32 size; /* Size of the buffer */
+};
+
+struct snd_sst_alloc_params_ext {
+ struct sst_address_info ring_buf_info[8];
+ u8 sg_count;
+ u8 reserved;
+ u16 reserved2;
+ u32 frag_size; /*Number of samples after which period elapsed
+ message is sent valid only if path = 0*/
+} __packed;
+
+struct snd_sst_stream_params {
+ union snd_sst_codec_params uc;
+} __packed;
+
+struct snd_sst_params {
+ u32 stream_id;
+ u8 codec;
+ u8 ops;
+ u8 stream_type;
+ u8 device_type;
+ struct snd_sst_stream_params sparams;
+ struct snd_sst_alloc_params_ext aparams;
+};
+
+#endif /* __SST_DSP_H__ */
/*
* sst_platform.c - Intel MID Platform driver
*
- * Copyright (C) 2010 Intel Corp
+ * Copyright (C) 2010-2012 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/compress_driver.h>
#include "sst_platform.h"
static struct sst_device *sst;
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
+{
+ .name = "Compress-cpu-dai",
+ .compress_dai = 1,
+ .playback = {
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
};
/* helper functions */
}
return retval;
}
+
+/* compress stream operations */
+static void sst_compr_fragment_elapsed(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("fragment elapsed by driver\n");
+ if (cstream)
+ snd_compr_fragment_elapsed(cstream);
+}
+
+static int sst_platform_compr_open(struct snd_compr_stream *cstream)
+{
+
+ int ret_val = 0;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ if (!sst || !try_module_get(sst->dev->driver->owner)) {
+ pr_err("no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->compr_ops = sst->compr_ops;
+
+ stream->id = 0;
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ runtime->private_data = stream;
+ return 0;
+out_ops:
+ kfree(stream);
+ return ret_val;
+}
+
+static int sst_platform_compr_free(struct snd_compr_stream *cstream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = cstream->runtime->private_data;
+ /*need to check*/
+ str_id = stream->id;
+ if (str_id)
+ ret_val = stream->compr_ops->close(str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+ pr_debug("%s: %d\n", __func__, ret_val);
+ return 0;
+}
+
+static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct sst_runtime_stream *stream;
+ int retval;
+ struct snd_sst_params str_params;
+ struct sst_compress_cb cb;
+
+ stream = cstream->runtime->private_data;
+ /* construct fw structure for this*/
+ memset(&str_params, 0, sizeof(str_params));
+
+ str_params.ops = STREAM_OPS_PLAYBACK;
+ str_params.stream_type = SST_STREAM_TYPE_MUSIC;
+ str_params.device_type = SND_SST_DEVICE_COMPRESS;
+
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_MP3: {
+ str_params.codec = SST_CODEC_TYPE_MP3;
+ str_params.sparams.uc.mp3_params.codec = SST_CODEC_TYPE_MP3;
+ str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AAC: {
+ str_params.codec = SST_CODEC_TYPE_AAC;
+ str_params.sparams.uc.aac_params.codec = SST_CODEC_TYPE_AAC;
+ str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
+ if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_ADTS;
+ else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_RAW;
+ else {
+ pr_err("Undefined format%d\n", params->codec.format);
+ return -EINVAL;
+ }
+ str_params.sparams.uc.aac_params.externalsr =
+ params->codec.sample_rate;
+ break;
+ }
+
+ default:
+ pr_err("codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ str_params.aparams.ring_buf_info[0].addr =
+ virt_to_phys(cstream->runtime->buffer);
+ str_params.aparams.ring_buf_info[0].size =
+ cstream->runtime->buffer_size;
+ str_params.aparams.sg_count = 1;
+ str_params.aparams.frag_size = cstream->runtime->fragment_size;
+
+ cb.param = cstream;
+ cb.compr_cb = sst_compr_fragment_elapsed;
+
+ retval = stream->compr_ops->open(&str_params, &cb);
+ if (retval < 0) {
+ pr_err("stream allocation failed %d\n", retval);
+ return retval;
+ }
+
+ stream->id = retval;
+ return 0;
+}
+
+static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->control(cmd, stream->id);
+}
+
+static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->tstamp(stream->id, tstamp);
+ tstamp->byte_offset = tstamp->copied_total %
+ (u32)cstream->runtime->buffer_size;
+ pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+ return 0;
+}
+
+static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
+ size_t bytes)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->ack(stream->id, (unsigned long)bytes);
+ stream->bytes_written += bytes;
+
+ return 0;
+}
+
+static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_caps(caps);
+}
+
+static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_codec_caps(codec);
+}
+
+static struct snd_compr_ops sst_platform_compr_ops = {
+
+ .open = sst_platform_compr_open,
+ .free = sst_platform_compr_free,
+ .set_params = sst_platform_compr_set_params,
+ .trigger = sst_platform_compr_trigger,
+ .pointer = sst_platform_compr_pointer,
+ .ack = sst_platform_compr_ack,
+ .get_caps = sst_platform_compr_get_caps,
+ .get_codec_caps = sst_platform_compr_get_codec_caps,
+};
+
static struct snd_soc_platform_driver sst_soc_platform_drv = {
.ops = &sst_platform_ops,
+ .compr_ops = &sst_platform_compr_ops,
.pcm_new = sst_pcm_new,
.pcm_free = sst_pcm_free,
};
#ifndef __SST_PLATFORMDRV_H__
#define __SST_PLATFORMDRV_H__
+#include "sst_dsp.h"
+
#define SST_MONO 1
#define SST_STEREO 2
#define SST_MAX_CAP 5
#define SST_MIN_PERIODS 2
#define SST_MAX_PERIODS (1024*2)
#define SST_FIFO_SIZE 0
-#define SST_CODEC_TYPE_PCM 1
struct pcm_stream_info {
int str_id;
SND_SST_DEVICE_VIBRA,
SND_SST_DEVICE_HAPTIC,
SND_SST_DEVICE_CAPTURE,
+ SND_SST_DEVICE_COMPRESS,
};
/* PCM Parameters */
struct sst_pcm_params sparams;
};
+struct sst_compress_cb {
+ void *param;
+ void (*compr_cb)(void *param);
+};
+
+struct compress_sst_ops {
+ const char *name;
+ int (*open) (struct snd_sst_params *str_params,
+ struct sst_compress_cb *cb);
+ int (*control) (unsigned int cmd, unsigned int str_id);
+ int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp);
+ int (*ack) (unsigned int str_id, unsigned long bytes);
+ int (*close) (unsigned int str_id);
+ int (*get_caps) (struct snd_compr_caps *caps);
+ int (*get_codec_caps) (struct snd_compr_codec_caps *codec);
+
+};
+
struct sst_ops {
int (*open) (struct sst_stream_params *str_param);
int (*device_control) (int cmd, void *arg);
struct sst_runtime_stream {
int stream_status;
+ unsigned int id;
+ size_t bytes_written;
struct pcm_stream_info stream_info;
struct sst_ops *ops;
+ struct compress_sst_ops *compr_ops;
spinlock_t status_lock;
};
char *name;
struct device *dev;
struct sst_ops *ops;
+ struct compress_sst_ops *compr_ops;
};
int sst_register_dsp(struct sst_device *sst);
return ret;
}
- saif->clk = clk_get(&pdev->dev, NULL);
+ saif->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(saif->clk)) {
ret = PTR_ERR(saif->clk);
dev_err(&pdev->dev, "Cannot get the clock: %d\n",
saif->base = devm_request_and_ioremap(&pdev->dev, iores);
if (!saif->base) {
dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENODEV;
- goto failed_get_resource;
+ return -ENODEV;
}
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
&saif->dma_param.chan_num);
if (ret) {
dev_err(&pdev->dev, "failed to get dma channel\n");
- goto failed_get_resource;
+ return ret;
}
} else {
saif->dma_param.chan_num = dmares->start;
ret = saif->irq;
dev_err(&pdev->dev, "failed to get irq resource: %d\n",
ret);
- goto failed_get_resource;
+ return ret;
}
saif->dev = &pdev->dev;
"mxs-saif", saif);
if (ret) {
dev_err(&pdev->dev, "failed to request irq\n");
- goto failed_get_resource;
+ return ret;
}
saif->dma_param.chan_irq = platform_get_irq(pdev, 1);
ret = saif->dma_param.chan_irq;
dev_err(&pdev->dev, "failed to get dma irq resource: %d\n",
ret);
- goto failed_get_resource;
+ return ret;
}
platform_set_drvdata(pdev, saif);
ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai);
if (ret) {
dev_err(&pdev->dev, "register DAI failed\n");
- goto failed_get_resource;
+ return ret;
}
ret = mxs_pcm_platform_register(&pdev->dev);
failed_pdev_alloc:
snd_soc_unregister_dai(&pdev->dev);
-failed_get_resource:
- clk_put(saif->clk);
return ret;
}
static int __devexit mxs_saif_remove(struct platform_device *pdev)
{
- struct mxs_saif *saif = platform_get_drvdata(pdev);
-
mxs_pcm_platform_unregister(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev);
- clk_put(saif->clk);
return 0;
}
config SND_OMAP_SOC
tristate "SoC Audio for the Texas Instruments OMAP chips"
- depends on ARCH_OMAP
+ depends on ARCH_OMAP && DMA_OMAP
+ select SND_SOC_DMAENGINE_PCM
config SND_OMAP_SOC_DMIC
tristate
help
Say Y if you want to add support for SoC audio on osk5912.
-config SND_OMAP_SOC_OVERO
- tristate "SoC Audio support for Gumstix Overo and CompuLab CM-T35"
- depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_OVERO || MACH_CM_T35)
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the
- Gumstix Overo or CompuLab CM-T35
-
-config SND_OMAP_SOC_OMAP3EVM
- tristate "SoC Audio support for OMAP3EVM board"
- depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3EVM
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the omap3evm board.
-
config SND_OMAP_SOC_AM3517EVM
tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
Say Y if you want to add support for SoC audio on Texas Instruments
SDP3430.
+config SND_OMAP_SOC_OMAP_TWL4030
+ tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
+ depends on TWL4030_CORE && SND_OMAP_SOC
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on TI SoC based boards
+ using twl4030 as c codec. This driver currently supports:
+ - Beagleboard or Devkit8000
+ - Gumstix Overo or CompuLab CM-T35/CM-T3730
+ - IGEP v2
+ - OMAP3EVM
+
config SND_OMAP_SOC_OMAP_ABE_TWL6040
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4
help
Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
-config SND_OMAP_SOC_OMAP3_BEAGLE
- tristate "SoC Audio support for OMAP3 Beagle and Devkit8000"
- depends on TWL4030_CORE && SND_OMAP_SOC
- depends on (MACH_OMAP3_BEAGLE || MACH_DEVKIT8000)
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the Beagleboard or
- the clone Devkit8000.
-
config SND_OMAP_SOC_ZOOM2
tristate "SoC Audio support for Zoom2"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
select SND_SOC_TWL4030
help
Say Y if you want to add support for Soc audio on Zoom2 board.
-
-config SND_OMAP_SOC_IGEP0020
- tristate "SoC Audio support for IGEP v2"
- depends on TWL4030_CORE && SND_OMAP_SOC && MACH_IGEP0020
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for Soc audio on IGEP v2 board.
snd-soc-rx51-objs := rx51.o
snd-soc-ams-delta-objs := ams-delta.o
snd-soc-osk5912-objs := osk5912.o
-snd-soc-overo-objs := overo.o
-snd-soc-omap3evm-objs := omap3evm.o
snd-soc-am3517evm-objs := am3517evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
+snd-soc-omap-twl4030-objs := omap-twl4030.o
snd-soc-omap3pandora-objs := omap3pandora.o
-snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
-snd-soc-igep0020-objs := igep0020.o
snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
-obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP3EVM) += snd-soc-omap3evm.o
obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
-obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret;
/* Set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
CODEC_CLOCK, SND_SOC_CLOCK_IN);
- if (ret < 0) {
+ if (ret < 0)
printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n");
- return ret;
- }
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
- return ret;
- }
-
- return 0;
+ return ret;
}
static struct snd_soc_ops am3517evm_ops = {
+++ /dev/null
-/*
- * igep0020.c -- SoC audio for IGEP v2
- *
- * Based on sound/soc/omap/overo.c by Steve Sakoman
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int igep2_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->codec_dai;
- int ret;
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops igep2_ops = {
- .hw_params = igep2_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link igep2_dai = {
- .name = "TWL4030",
- .stream_name = "TWL4030",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &igep2_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_card_igep2 = {
- .name = "igep2",
- .owner = THIS_MODULE,
- .dai_link = &igep2_dai,
- .num_links = 1,
-};
-
-static struct platform_device *igep2_snd_device;
-
-static int __init igep2_soc_init(void)
-{
- int ret;
-
- if (!machine_is_igep0020())
- return -ENODEV;
- printk(KERN_INFO "IGEP v2 SoC init\n");
-
- igep2_snd_device = platform_device_alloc("soc-audio", -1);
- if (!igep2_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(igep2_snd_device, &snd_soc_card_igep2);
-
- ret = platform_device_add(igep2_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(igep2_snd_device);
-
- return ret;
-}
-module_init(igep2_soc_init);
-
-static void __exit igep2_soc_exit(void)
-{
- platform_device_unregister(igep2_snd_device);
-}
-module_exit(igep2_soc_exit);
-
-MODULE_AUTHOR("Enric Balletbo i Serra <eballetbo@iseebcn.com>");
-MODULE_DESCRIPTION("ALSA SoC IGEP v2");
-MODULE_LICENSE("GPL");
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
{
+ struct clk *fck_src;
const char *src;
+ int r;
if (fck_src_id == MCBSP_CLKS_PAD_SRC)
- src = "clks_ext";
+ src = "pad_fck";
else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
- src = "clks_fclk";
+ src = "prcm_fck";
else
return -EINVAL;
- if (mcbsp->pdata->set_clk_src)
- return mcbsp->pdata->set_clk_src(mcbsp->dev, mcbsp->fclk, src);
- else
+ fck_src = clk_get(mcbsp->dev, src);
+ if (IS_ERR(fck_src)) {
+ dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
return -EINVAL;
-}
-
-int omap_mcbsp_6pin_src_mux(struct omap_mcbsp *mcbsp, u8 mux)
-{
- const char *signal, *src;
+ }
- if (!mcbsp->pdata->mux_signal)
- return -EINVAL;
+ pm_runtime_put_sync(mcbsp->dev);
- switch (mux) {
- case CLKR_SRC_CLKR:
- signal = "clkr";
- src = "clkr";
- break;
- case CLKR_SRC_CLKX:
- signal = "clkr";
- src = "clkx";
- break;
- case FSR_SRC_FSR:
- signal = "fsr";
- src = "fsr";
- break;
- case FSR_SRC_FSX:
- signal = "fsr";
- src = "fsx";
- break;
- default:
- return -EINVAL;
+ r = clk_set_parent(mcbsp->fclk, fck_src);
+ if (r) {
+ dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
+ src);
+ clk_put(fck_src);
+ return r;
}
- return mcbsp->pdata->mux_signal(mcbsp->dev, signal, src);
+ pm_runtime_get_sync(mcbsp->dev);
+
+ clk_put(fck_src);
+
+ return 0;
+
}
#define max_thres(m) (mcbsp->pdata->buffer_size)
/* McBSP functional clock source changing function */
int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id);
-/* McBSP signal muxing API */
-int omap_mcbsp_6pin_src_mux(struct omap_mcbsp *mcbsp, u8 mux);
-
/* Sidetone specific API */
int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain);
int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain);
#include <linux/mfd/twl6040.h>
#include <linux/platform_data/omap-abe-twl6040.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
struct abe_twl6040 {
int jack_detection; /* board can detect jack events */
int mclk_freq; /* MCLK frequency speed for twl6040 */
+
+ struct platform_device *dmic_codec_dev;
};
static int omap_abe_hw_params(struct snd_pcm_substream *substream,
int hs_trim;
int ret = 0;
- /* Disable not connected paths if not used */
- twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
- twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
- twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
- twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
- twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vinrator");
- twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
- twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
-
/*
* Configure McPDM offset cancellation based on the HSOTRIM value from
* twl6040.
twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
}
+ /*
+ * NULL pdata means we booted with DT. In this case the routing is
+ * provided and the card is fully routed, no need to mark pins.
+ */
+ if (!pdata)
+ return ret;
+
+ /* Disable not connected paths if not used */
+ twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
+ twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
+ twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
+ twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
+ twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vinrator");
+ twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
+ twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
+ twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
+ twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
+
return ret;
}
static __devinit int omap_abe_probe(struct platform_device *pdev)
{
struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
struct snd_soc_card *card = &omap_abe_card;
struct abe_twl6040 *priv;
int num_links = 0;
- int ret;
+ int ret = 0;
card->dev = &pdev->dev;
- if (!pdata) {
- dev_err(&pdev->dev, "Missing pdata\n");
- return -ENODEV;
- }
-
priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- if (pdata->card_name) {
- card->name = pdata->card_name;
+ priv->dmic_codec_dev = ERR_PTR(-EINVAL);
+
+ if (node) {
+ struct device_node *dai_node;
+
+ if (snd_soc_of_parse_card_name(card, "ti,model")) {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card,
+ "ti,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Error while parsing DAPM routing\n");
+ return ret;
+ }
+
+ dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McPDM node is not provided\n");
+ return -EINVAL;
+ }
+ abe_twl6040_dai_links[0].cpu_dai_name = NULL;
+ abe_twl6040_dai_links[0].cpu_of_node = dai_node;
+
+ dai_node = of_parse_phandle(node, "ti,dmic", 0);
+ if (dai_node) {
+ num_links = 2;
+ abe_twl6040_dai_links[1].cpu_dai_name = NULL;
+ abe_twl6040_dai_links[1].cpu_of_node = dai_node;
+
+ priv->dmic_codec_dev = platform_device_register_simple(
+ "dmic-codec", -1, NULL, 0);
+ if (IS_ERR(priv->dmic_codec_dev)) {
+ dev_err(&pdev->dev,
+ "Can't instantiate dmic-codec\n");
+ return PTR_ERR(priv->dmic_codec_dev);
+ }
+ } else {
+ num_links = 1;
+ }
+
+ of_property_read_u32(node, "ti,jack-detection",
+ &priv->jack_detection);
+ of_property_read_u32(node, "ti,mclk-freq",
+ &priv->mclk_freq);
+ if (!priv->mclk_freq) {
+ dev_err(&pdev->dev, "MCLK frequency not provided\n");
+ ret = -EINVAL;
+ goto err_unregister;
+ }
+
+ omap_abe_card.fully_routed = 1;
+ } else if (pdata) {
+ if (pdata->card_name) {
+ card->name = pdata->card_name;
+ } else {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ if (pdata->has_dmic)
+ num_links = 2;
+ else
+ num_links = 1;
+
+ priv->jack_detection = pdata->jack_detection;
+ priv->mclk_freq = pdata->mclk_freq;
} else {
- dev_err(&pdev->dev, "Card name is not provided\n");
+ dev_err(&pdev->dev, "Missing pdata\n");
return -ENODEV;
}
- priv->jack_detection = pdata->jack_detection;
- priv->mclk_freq = pdata->mclk_freq;
-
if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency missing\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_unregister;
}
- if (pdata->has_dmic)
- num_links = 2;
- else
- num_links = 1;
-
card->dai_link = abe_twl6040_dai_links;
card->num_links = num_links;
snd_soc_card_set_drvdata(card, priv);
ret = snd_soc_register_card(card);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ if (!IS_ERR(priv->dmic_codec_dev))
+ platform_device_unregister(priv->dmic_codec_dev);
return ret;
}
static int __devexit omap_abe_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
snd_soc_unregister_card(card);
+ if (!IS_ERR(priv->dmic_codec_dev))
+ platform_device_unregister(priv->dmic_codec_dev);
+
return 0;
}
+static const struct of_device_id omap_abe_of_match[] = {
+ {.compatible = "ti,abe-twl6040", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_abe_of_match);
+
static struct platform_driver omap_abe_driver = {
.driver = {
.name = "omap-abe-twl6040",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
+ .of_match_table = omap_abe_of_match,
},
.probe = omap_abe_probe,
.remove = __devexit_p(omap_abe_remove),
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
-#include <plat/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
*/
static struct omap_pcm_dma_data omap_dmic_dai_dma_params = {
.name = "DMIC capture",
- .data_type = OMAP_DMA_DATA_TYPE_S32,
- .sync_mode = OMAP_DMA_SYNC_PACKET,
};
static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
mutex_unlock(&dmic->mutex);
+ snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params);
return ret;
}
struct snd_soc_dai *dai)
{
struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+ struct omap_pcm_dma_data *dma_data;
int channels;
dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
}
/* packet size is threshold * channels */
- omap_dmic_dai_dma_params.packet_size = dmic->threshold * channels;
- snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params);
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->packet_size = dmic->threshold * channels;
return 0;
}
#include <sound/asoundef.h>
#include <video/omapdss.h>
-#include <plat/dma.h>
#include "omap-pcm.h"
#include "omap-hdmi.h"
dev_err(dai->dev, "audio not supported\n");
return -ENODEV;
}
+
+ snd_soc_dai_set_dma_data(dai, substream, &priv->dma_params);
+
return 0;
}
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
struct snd_aes_iec958 *iec = &priv->iec;
struct snd_cea_861_aud_if *cea = &priv->cea;
+ struct omap_pcm_dma_data *dma_data;
int err = 0;
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- priv->dma_params.packet_size = 16;
+ dma_data->packet_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- priv->dma_params.packet_size = 32;
+ dma_data->packet_size = 32;
break;
default:
dev_err(dai->dev, "format not supported!\n");
return -EINVAL;
}
- priv->dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
-
- snd_soc_dai_set_dma_data(dai, substream,
- &priv->dma_params);
+ dma_data->data_type = 32;
/*
* fill the IEC-60958 channel status word
hdmi_data->dma_params.dma_req = hdmi_rsrc->start;
hdmi_data->dma_params.name = "HDMI playback";
- hdmi_data->dma_params.sync_mode = OMAP_DMA_SYNC_PACKET;
/*
* TODO: We assume that there is only one DSS HDMI device. Future
#include <linux/module.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <plat/cpu.h>
-#include <plat/dma.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
#include "mcbsp.h"
#include "omap-mcbsp.h"
*/
if (dma_data->packet_size)
words = dma_data->packet_size;
- else if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
- words = snd_pcm_lib_period_bytes(substream) /
- (mcbsp->wlen / 8);
else
words = 1;
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
}
+ snd_soc_dai_set_dma_data(cpu_dai, substream,
+ &mcbsp->dma_data[substream->stream]);
+
return err;
}
struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
struct omap_pcm_dma_data *dma_data;
- int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
+ int wlen, channels, wpf;
int pkt_size = 0;
unsigned int format, div, framesize, master;
- dma_data = &mcbsp->dma_data[substream->stream];
+ dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
channels = params_channels(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- dma_data->data_type = OMAP_DMA_DATA_TYPE_S16;
wlen = 16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- dma_data->data_type = OMAP_DMA_DATA_TYPE_S32;
wlen = 32;
break;
default:
dma_data->set_threshold = omap_mcbsp_set_threshold;
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
int period_words, max_thrsh;
+ int divider = 0;
period_words = params_period_bytes(params) / (wlen / 8);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
else
max_thrsh = mcbsp->max_rx_thres;
/*
- * If the period contains less or equal number of words,
- * we are using the original threshold mode setup:
- * McBSP threshold = sDMA frame size = period_size
- * Otherwise we switch to sDMA packet mode:
- * McBSP threshold = sDMA packet size
- * sDMA frame size = period size
+ * Use sDMA packet mode if McBSP is in threshold mode:
+ * If period words less than the FIFO size the packet
+ * size is set to the number of period words, otherwise
+ * Look for the biggest threshold value which divides
+ * the period size evenly.
*/
- if (period_words > max_thrsh) {
- int divider = 0;
-
- /*
- * Look for the biggest threshold value, which
- * divides the period size evenly.
- */
- divider = period_words / max_thrsh;
- if (period_words % max_thrsh)
- divider++;
- while (period_words % divider &&
- divider < period_words)
- divider++;
- if (divider == period_words)
- return -EINVAL;
-
- pkt_size = period_words / divider;
- sync_mode = OMAP_DMA_SYNC_PACKET;
- } else {
- sync_mode = OMAP_DMA_SYNC_FRAME;
- }
+ divider = period_words / max_thrsh;
+ if (period_words % max_thrsh)
+ divider++;
+ while (period_words % divider &&
+ divider < period_words)
+ divider++;
+ if (divider == period_words)
+ return -EINVAL;
+
+ pkt_size = period_words / divider;
} else if (channels > 1) {
/* Use packet mode for non mono streams */
pkt_size = channels;
- sync_mode = OMAP_DMA_SYNC_PACKET;
}
}
- dma_data->sync_mode = sync_mode;
dma_data->packet_size = pkt_size;
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
-
if (mcbsp->configured) {
/* McBSP already configured by another stream */
return 0;
/* Generic McBSP register settings */
regs->spcr2 |= XINTM(3) | FREE;
regs->spcr1 |= RINTM(3);
- /* RFIG and XFIG are not defined in 34xx */
- if (!cpu_is_omap34xx() && !cpu_is_omap44xx()) {
+ /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
+ if (!mcbsp->pdata->has_ccr) {
regs->rcr2 |= RFIG;
regs->xcr2 |= XFIG;
}
- if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
+
+ /* Configure XCCR/RCCR only for revisions which have ccr registers */
+ if (mcbsp->pdata->has_ccr) {
regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
}
return -EBUSY;
}
- if (clk_id == OMAP_MCBSP_SYSCLK_CLK ||
- clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK ||
- clk_id == OMAP_MCBSP_SYSCLK_CLKS_EXT ||
- clk_id == OMAP_MCBSP_SYSCLK_CLKX_EXT ||
- clk_id == OMAP_MCBSP_SYSCLK_CLKR_EXT) {
- mcbsp->in_freq = freq;
- regs->srgr2 &= ~CLKSM;
- regs->pcr0 &= ~SCLKME;
- } else if (cpu_class_is_omap1()) {
- /*
- * McBSP CLKR/FSR signal muxing functions are only available on
- * OMAP2 or newer versions
- */
- return -EINVAL;
- }
+ mcbsp->in_freq = freq;
+ regs->srgr2 &= ~CLKSM;
+ regs->pcr0 &= ~SCLKME;
switch (clk_id) {
case OMAP_MCBSP_SYSCLK_CLK:
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
regs->pcr0 |= SCLKME;
break;
-
-
- case OMAP_MCBSP_CLKR_SRC_CLKR:
- err = omap_mcbsp_6pin_src_mux(mcbsp, CLKR_SRC_CLKR);
- break;
- case OMAP_MCBSP_CLKR_SRC_CLKX:
- err = omap_mcbsp_6pin_src_mux(mcbsp, CLKR_SRC_CLKX);
- break;
- case OMAP_MCBSP_FSR_SRC_FSR:
- err = omap_mcbsp_6pin_src_mux(mcbsp, FSR_SRC_FSR);
- break;
- case OMAP_MCBSP_FSR_SRC_FSX:
- err = omap_mcbsp_6pin_src_mux(mcbsp, FSR_SRC_FSX);
- break;
default:
err = -ENODEV;
}
return 0;
}
-#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(channel) \
+#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
static int \
-omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
struct snd_ctl_elem_value *uc) \
{ \
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
\
/* OMAP McBSP implementation uses index values 0..4 */ \
return omap_st_set_chgain(mcbsp, channel, val); \
-}
-
-#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(channel) \
+} \
+ \
static int \
-omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
struct snd_ctl_elem_value *uc) \
{ \
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
return 0; \
}
-OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(0)
-OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(1)
-OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(0)
-OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(1)
+OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
+OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
return 0;
}
-static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = {
- SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 1, 0, 1, 0,
- omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
- OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume",
- -32768, 32767,
- omap_mcbsp_get_st_ch0_volume,
- omap_mcbsp_set_st_ch0_volume),
- OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume",
- -32768, 32767,
- omap_mcbsp_get_st_ch1_volume,
- omap_mcbsp_set_st_ch1_volume),
-};
+#define OMAP_MCBSP_ST_CONTROLS(port) \
+static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
+SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \
+ omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \
+OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
+ -32768, 32767, \
+ omap_mcbsp_get_st_ch0_volume, \
+ omap_mcbsp_set_st_ch0_volume), \
+OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
+ -32768, 32767, \
+ omap_mcbsp_get_st_ch1_volume, \
+ omap_mcbsp_set_st_ch1_volume), \
+}
-static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = {
- SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 2, 0, 1, 0,
- omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
- OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume",
- -32768, 32767,
- omap_mcbsp_get_st_ch0_volume,
- omap_mcbsp_set_st_ch0_volume),
- OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume",
- -32768, 32767,
- omap_mcbsp_get_st_ch1_volume,
- omap_mcbsp_set_st_ch1_volume),
-};
+OMAP_MCBSP_ST_CONTROLS(2);
+OMAP_MCBSP_ST_CONTROLS(3);
int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- if (!mcbsp->st_data)
- return -ENODEV;
+ if (!mcbsp->st_data) {
+ dev_warn(mcbsp->dev, "No sidetone data for port\n");
+ return 0;
+ }
- switch (cpu_dai->id) {
+ switch (mcbsp->id) {
case 2: /* McBSP 2 */
return snd_soc_add_dai_controls(cpu_dai,
omap_mcbsp2_st_controls,
}
EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
+static struct omap_mcbsp_platform_data omap2420_pdata = {
+ .reg_step = 4,
+ .reg_size = 2,
+};
+
+static struct omap_mcbsp_platform_data omap2430_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+};
+
+static struct omap_mcbsp_platform_data omap3_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+ .has_wakeup = true,
+};
+
+static struct omap_mcbsp_platform_data omap4_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+ .has_wakeup = true,
+};
+
+static const struct of_device_id omap_mcbsp_of_match[] = {
+ {
+ .compatible = "ti,omap2420-mcbsp",
+ .data = &omap2420_pdata,
+ },
+ {
+ .compatible = "ti,omap2430-mcbsp",
+ .data = &omap2430_pdata,
+ },
+ {
+ .compatible = "ti,omap3-mcbsp",
+ .data = &omap3_pdata,
+ },
+ {
+ .compatible = "ti,omap4-mcbsp",
+ .data = &omap4_pdata,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match);
+
static __devinit int asoc_mcbsp_probe(struct platform_device *pdev)
{
struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct omap_mcbsp *mcbsp;
+ const struct of_device_id *match;
int ret;
- if (!pdata) {
+ match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
+ if (match) {
+ struct device_node *node = pdev->dev.of_node;
+ int buffer_size;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct omap_mcbsp_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ memcpy(pdata, match->data, sizeof(*pdata));
+ if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
+ pdata->buffer_size = buffer_size;
+ } else if (!pdata) {
dev_err(&pdev->dev, "missing platform data.\n");
return -EINVAL;
}
.driver = {
.name = "omap-mcbsp",
.owner = THIS_MODULE,
+ .of_match_table = omap_mcbsp_of_match,
},
.probe = asoc_mcbsp_probe,
OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
- OMAP_MCBSP_CLKR_SRC_CLKR, /* CLKR from CLKR pin */
- OMAP_MCBSP_CLKR_SRC_CLKX, /* CLKR from CLKX pin */
- OMAP_MCBSP_FSR_SRC_FSR, /* FSR from FSR pin */
- OMAP_MCBSP_FSR_SRC_FSX, /* FSR from FSX pin */
};
/* McBSP dividers */
OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
};
-#if defined(CONFIG_SOC_OMAP2420)
-#define NUM_LINKS 2
-#endif
-#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
-#undef NUM_LINKS
-#define NUM_LINKS 3
-#endif
-#if defined(CONFIG_ARCH_OMAP4)
-#undef NUM_LINKS
-#define NUM_LINKS 4
-#endif
-#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_OMAP2430)
-#undef NUM_LINKS
-#define NUM_LINKS 5
-#endif
-
int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd);
#endif
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <plat/dma.h>
#include <plat/omap_hwmod.h>
#include "omap-mcpdm.h"
#include "omap-pcm.h"
static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
{
.name = "Audio playback",
- .dma_req = OMAP44XX_DMA_MCPDM_DL,
- .data_type = OMAP_DMA_DATA_TYPE_S32,
- .sync_mode = OMAP_DMA_SYNC_PACKET,
- .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_DN_DATA,
},
{
.name = "Audio capture",
- .dma_req = OMAP44XX_DMA_MCPDM_UP,
- .data_type = OMAP_DMA_DATA_TYPE_S32,
- .sync_mode = OMAP_DMA_SYNC_PACKET,
- .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_UP_DATA,
},
};
}
omap_mcpdm_open_streams(mcpdm);
}
-
mutex_unlock(&mcpdm->mutex);
+ snd_soc_dai_set_dma_data(dai, substream,
+ &omap_mcpdm_dai_dma_params[substream->stream]);
+
return 0;
}
return -EINVAL;
}
- dma_data = &omap_mcpdm_dai_dma_params[stream];
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
/* Configure McPDM channels, and DMA packet size */
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
dma_data->packet_size = mcpdm->up_threshold * channels;
}
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
-
return 0;
}
{
struct omap_mcpdm *mcpdm;
struct resource *res;
- int ret = 0;
- mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
+ mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
if (!mcpdm)
return -ENOMEM;
mutex_init(&mcpdm->mutex);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+ if (res == NULL)
+ return -ENOMEM;
+
+ omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA;
+ omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "no resource\n");
- goto err_res;
- }
+ if (res == NULL)
+ return -ENOMEM;
- if (!request_mem_region(res->start, resource_size(res), "McPDM")) {
- ret = -EBUSY;
- goto err_res;
- }
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link");
+ if (!res)
+ return -ENODEV;
- mcpdm->io_base = ioremap(res->start, resource_size(res));
- if (!mcpdm->io_base) {
- ret = -ENOMEM;
- goto err_iomap;
- }
+ omap_mcpdm_dai_dma_params[0].dma_req = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "up_link");
+ if (!res)
+ return -ENODEV;
+
+ omap_mcpdm_dai_dma_params[1].dma_req = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (res == NULL)
+ return -ENOMEM;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), "McPDM"))
+ return -EBUSY;
+
+ mcpdm->io_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!mcpdm->io_base)
+ return -ENOMEM;
mcpdm->irq = platform_get_irq(pdev, 0);
- if (mcpdm->irq < 0) {
- ret = mcpdm->irq;
- goto err_irq;
- }
+ if (mcpdm->irq < 0)
+ return mcpdm->irq;
mcpdm->dev = &pdev->dev;
- ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
- if (!ret)
- return 0;
-
-err_irq:
- iounmap(mcpdm->io_base);
-err_iomap:
- release_mem_region(res->start, resource_size(res));
-err_res:
- kfree(mcpdm);
- return ret;
+ return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
}
static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
{
- struct omap_mcpdm *mcpdm = platform_get_drvdata(pdev);
- struct resource *res;
-
snd_soc_unregister_dai(&pdev->dev);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iounmap(mcpdm->io_base);
- release_mem_region(res->start, resource_size(res));
-
- kfree(mcpdm);
return 0;
}
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/omap-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
#include <plat/cpu.h>
-#include <plat/dma.h>
#include "omap-pcm.h"
static const struct snd_pcm_hardware omap_pcm_hardware = {
.buffer_bytes_max = 128 * 1024,
};
-struct omap_runtime_data {
- spinlock_t lock;
- struct omap_pcm_dma_data *dma_data;
- int dma_ch;
- int period_index;
-};
-
-static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
+static int omap_pcm_get_dma_buswidth(int num_bits)
{
- struct snd_pcm_substream *substream = data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd = runtime->private_data;
- unsigned long flags;
-
- if ((cpu_is_omap1510())) {
- /*
- * OMAP1510 doesn't fully support DMA progress counter
- * and there is no software emulation implemented yet,
- * so have to maintain our own progress counters
- * that can be used by omap_pcm_pointer() instead.
- */
- spin_lock_irqsave(&prtd->lock, flags);
- if ((stat == OMAP_DMA_LAST_IRQ) &&
- (prtd->period_index == runtime->periods - 1)) {
- /* we are in sync, do nothing */
- spin_unlock_irqrestore(&prtd->lock, flags);
- return;
- }
- if (prtd->period_index >= 0) {
- if (stat & OMAP_DMA_BLOCK_IRQ) {
- /* end of buffer reached, loop back */
- prtd->period_index = 0;
- } else if (stat & OMAP_DMA_LAST_IRQ) {
- /* update the counter for the last period */
- prtd->period_index = runtime->periods - 1;
- } else if (++prtd->period_index >= runtime->periods) {
- /* end of buffer missed? loop back */
- prtd->period_index = 0;
- }
- }
- spin_unlock_irqrestore(&prtd->lock, flags);
- }
+ int buswidth;
- snd_pcm_period_elapsed(substream);
+ switch (num_bits) {
+ case 16:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case 32:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ buswidth = -EINVAL;
+ break;
+ }
+ return buswidth;
}
+
/* this may get called several times by oss emulation */
static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct omap_runtime_data *prtd = runtime->private_data;
struct omap_pcm_dma_data *dma_data;
-
+ struct dma_slave_config config;
+ struct dma_chan *chan;
int err = 0;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
- if (prtd->dma_data)
- return 0;
- prtd->dma_data = dma_data;
- err = omap_request_dma(dma_data->dma_req, dma_data->name,
- omap_pcm_dma_irq, substream, &prtd->dma_ch);
- if (!err) {
- /*
- * Link channel with itself so DMA doesn't need any
- * reprogramming while looping the buffer
- */
- omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
- }
+ chan = snd_dmaengine_pcm_get_chan(substream);
+ if (!chan)
+ return -EINVAL;
- return err;
-}
+ /* fills in addr_width and direction */
+ err = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (err)
+ return err;
-static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd = runtime->private_data;
+ /* Override the *_dma addr_width if requested by the DAI driver */
+ if (dma_data->data_type) {
+ int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type);
- if (prtd->dma_data == NULL)
- return 0;
-
- omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
- omap_free_dma(prtd->dma_ch);
- prtd->dma_data = NULL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ config.dst_addr_width = buswidth;
+ else
+ config.src_addr_width = buswidth;
+ }
- snd_pcm_set_runtime_buffer(substream, NULL);
+ config.src_addr = dma_data->port_addr;
+ config.dst_addr = dma_data->port_addr;
+ config.src_maxburst = dma_data->packet_size;
+ config.dst_maxburst = dma_data->packet_size;
- return 0;
+ return dmaengine_slave_config(chan, &config);
}
-static int omap_pcm_prepare(struct snd_pcm_substream *substream)
+static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd = runtime->private_data;
- struct omap_pcm_dma_data *dma_data = prtd->dma_data;
- struct omap_dma_channel_params dma_params;
- int bytes;
-
- /* return if this is a bufferless transfer e.g.
- * codec <--> BT codec or GSM modem -- lg FIXME */
- if (!prtd->dma_data)
- return 0;
-
- memset(&dma_params, 0, sizeof(dma_params));
- dma_params.data_type = dma_data->data_type;
- dma_params.trigger = dma_data->dma_req;
- dma_params.sync_mode = dma_data->sync_mode;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
- dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
- dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
- dma_params.src_start = runtime->dma_addr;
- dma_params.dst_start = dma_data->port_addr;
- dma_params.dst_port = OMAP_DMA_PORT_MPUI;
- dma_params.dst_fi = dma_data->packet_size;
- } else {
- dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
- dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
- dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
- dma_params.src_start = dma_data->port_addr;
- dma_params.dst_start = runtime->dma_addr;
- dma_params.src_port = OMAP_DMA_PORT_MPUI;
- dma_params.src_fi = dma_data->packet_size;
- }
- /*
- * Set DMA transfer frame size equal to ALSA period size and frame
- * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
- * we can transfer the whole ALSA buffer with single DMA transfer but
- * still can get an interrupt at each period bounary
- */
- bytes = snd_pcm_lib_period_bytes(substream);
- dma_params.elem_count = bytes >> dma_data->data_type;
- dma_params.frame_count = runtime->periods;
- omap_set_dma_params(prtd->dma_ch, &dma_params);
-
- if ((cpu_is_omap1510()))
- omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
- OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
- else if (!substream->runtime->no_period_wakeup)
- omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
- else {
- /*
- * No period wakeup:
- * we need to disable BLOCK_IRQ, which is enabled by the omap
- * dma core at request dma time.
- */
- omap_disable_dma_irq(prtd->dma_ch, OMAP_DMA_BLOCK_IRQ);
- }
-
- if (!(cpu_class_is_omap1())) {
- omap_set_dma_src_burst_mode(prtd->dma_ch,
- OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(prtd->dma_ch,
- OMAP_DMA_DATA_BURST_16);
- }
-
+ snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd = runtime->private_data;
- struct omap_pcm_dma_data *dma_data = prtd->dma_data;
- unsigned long flags;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct omap_pcm_dma_data *dma_data;
int ret = 0;
- spin_lock_irqsave(&prtd->lock, flags);
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- prtd->period_index = 0;
/* Configure McBSP internal buffer usage */
if (dma_data->set_threshold)
dma_data->set_threshold(substream);
-
- omap_start_dma(prtd->dma_ch);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- prtd->period_index = -1;
- omap_stop_dma(prtd->dma_ch);
break;
default:
ret = -EINVAL;
}
- spin_unlock_irqrestore(&prtd->lock, flags);
+
+ if (ret == 0)
+ ret = snd_dmaengine_pcm_trigger(substream, cmd);
return ret;
}
static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd = runtime->private_data;
- dma_addr_t ptr;
snd_pcm_uframes_t offset;
- if (cpu_is_omap1510()) {
- offset = prtd->period_index * runtime->period_size;
- } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- ptr = omap_get_dma_dst_pos(prtd->dma_ch);
- offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
- } else {
- ptr = omap_get_dma_src_pos(prtd->dma_ch);
- offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
- }
-
- if (offset >= runtime->buffer_size)
- offset = 0;
+ if (cpu_is_omap1510())
+ offset = snd_dmaengine_pcm_pointer_no_residue(substream);
+ else
+ offset = snd_dmaengine_pcm_pointer(substream);
return offset;
}
static int omap_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct omap_runtime_data *prtd;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct omap_pcm_dma_data *dma_data;
int ret;
snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
+ return ret;
- prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- spin_lock_init(&prtd->lock);
- runtime->private_data = prtd;
-
-out:
+ dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
+ &dma_data->dma_req);
return ret;
}
static int omap_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- kfree(runtime->private_data);
+ snd_dmaengine_pcm_close(substream);
return 0;
}
.ioctl = snd_pcm_lib_ioctl,
.hw_params = omap_pcm_hw_params,
.hw_free = omap_pcm_hw_free,
- .prepare = omap_pcm_prepare,
.trigger = omap_pcm_trigger,
.pointer = omap_pcm_pointer,
.mmap = omap_pcm_mmap,
int dma_req; /* DMA request line */
unsigned long port_addr; /* transmit/receive register */
void (*set_threshold)(struct snd_pcm_substream *substream);
- int data_type; /* data type 8,16,32 */
- int sync_mode; /* DMA sync mode */
+ int data_type; /* 8, 16, 32 (bits) or 0 to let omap-pcm
+ * to decide the sDMA data type */
int packet_size; /* packet size only in PACKET mode */
};
--- /dev/null
+/*
+ * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * All rights reserved.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This driver replaces the following machine drivers:
+ * omap3beagle (Author: Steve Sakoman <steve@sakoman.com>)
+ * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
+ * overo (Author: Steve Sakoman <steve@sakoman.com>)
+ * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/platform_data/omap-twl4030.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+
+static int omap_twl4030_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->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ unsigned int fmt;
+ int ret;
+
+ switch (params_channels(params)) {
+ case 2: /* Stereo I2S mode */
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ case 4: /* Four channel TDM mode */
+ fmt = SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops omap_twl4030_ops = {
+ .hw_params = omap_twl4030_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
+ {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai_name = "omap-mcbsp.2",
+ .codec_dai_name = "twl4030-hifi",
+ .platform_name = "omap-pcm-audio",
+ .codec_name = "twl4030-codec",
+ .ops = &omap_twl4030_ops,
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card omap_twl4030_card = {
+ .owner = THIS_MODULE,
+ .dai_link = omap_twl4030_dai_links,
+ .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
+};
+
+static __devinit int omap_twl4030_probe(struct platform_device *pdev)
+{
+ struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ struct snd_soc_card *card = &omap_twl4030_card;
+ int ret = 0;
+
+ card->dev = &pdev->dev;
+
+ if (node) {
+ struct device_node *dai_node;
+
+ if (snd_soc_of_parse_card_name(card, "ti,model")) {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McBSP node is not provided\n");
+ return -EINVAL;
+ }
+ omap_twl4030_dai_links[0].cpu_dai_name = NULL;
+ omap_twl4030_dai_links[0].cpu_of_node = dai_node;
+
+ } else if (pdata) {
+ if (pdata->card_name) {
+ card->name = pdata->card_name;
+ } else {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+ } else {
+ dev_err(&pdev->dev, "Missing pdata\n");
+ return -ENODEV;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit omap_twl4030_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static const struct of_device_id omap_twl4030_of_match[] = {
+ {.compatible = "ti,omap-twl4030", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
+
+static struct platform_driver omap_twl4030_driver = {
+ .driver = {
+ .name = "omap-twl4030",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = omap_twl4030_of_match,
+ },
+ .probe = omap_twl4030_probe,
+ .remove = __devexit_p(omap_twl4030_remove),
+};
+
+module_platform_driver(omap_twl4030_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-twl4030");
+++ /dev/null
-/*
- * omap3beagle.c -- SoC audio for OMAP3 Beagle
- *
- * Author: Steve Sakoman <steve@sakoman.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int omap3beagle_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->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int fmt;
- int ret;
-
- switch (params_channels(params)) {
- case 2: /* Stereo I2S mode */
- fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
- break;
- case 4: /* Four channel TDM mode */
- fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
- break;
- default:
- return -EINVAL;
- }
-
- /* Set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, fmt);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec DAI configuration\n");
- return ret;
- }
-
- /* Set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
- if (ret < 0) {
- printk(KERN_ERR "can't set cpu DAI configuration\n");
- return ret;
- }
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops omap3beagle_ops = {
- .hw_params = omap3beagle_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link omap3beagle_dai = {
- .name = "TWL4030",
- .stream_name = "TWL4030",
- .cpu_dai_name = "omap-mcbsp.2",
- .platform_name = "omap-pcm-audio",
- .codec_dai_name = "twl4030-hifi",
- .codec_name = "twl4030-codec",
- .ops = &omap3beagle_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_omap3beagle = {
- .name = "omap3beagle",
- .owner = THIS_MODULE,
- .dai_link = &omap3beagle_dai,
- .num_links = 1,
-};
-
-static struct platform_device *omap3beagle_snd_device;
-
-static int __init omap3beagle_soc_init(void)
-{
- int ret;
-
- if (!(machine_is_omap3_beagle() || machine_is_devkit8000()))
- return -ENODEV;
- pr_info("OMAP3 Beagle/Devkit8000 SoC init\n");
-
- omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
- if (!omap3beagle_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(omap3beagle_snd_device, &snd_soc_omap3beagle);
-
- ret = platform_device_add(omap3beagle_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(omap3beagle_snd_device);
-
- return ret;
-}
-
-static void __exit omap3beagle_soc_exit(void)
-{
- platform_device_unregister(omap3beagle_snd_device);
-}
-
-module_init(omap3beagle_soc_init);
-module_exit(omap3beagle_soc_exit);
-
-MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
-MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * omap3evm.c -- ALSA SoC support for OMAP3 EVM
- *
- * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
- *
- * Based on sound/soc/omap/beagle.c by Steve Sakoman
- *
- * Copyright (C) 2008 Texas Instruments, Incorporated
- *
- * 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 version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
- * whether express or implied; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int omap3evm_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->codec_dai;
- int ret;
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "Can't set codec system clock\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops omap3evm_ops = {
- .hw_params = omap3evm_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link omap3evm_dai = {
- .name = "TWL4030",
- .stream_name = "TWL4030",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &omap3evm_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_omap3evm = {
- .name = "omap3evm",
- .owner = THIS_MODULE,
- .dai_link = &omap3evm_dai,
- .num_links = 1,
-};
-
-static struct platform_device *omap3evm_snd_device;
-
-static int __init omap3evm_soc_init(void)
-{
- int ret;
-
- if (!machine_is_omap3evm())
- return -ENODEV;
- pr_info("OMAP3 EVM SoC init\n");
-
- omap3evm_snd_device = platform_device_alloc("soc-audio", -1);
- if (!omap3evm_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(omap3evm_snd_device, &snd_soc_omap3evm);
- ret = platform_device_add(omap3evm_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(omap3evm_snd_device);
-
- return ret;
-}
-
-static void __exit omap3evm_soc_exit(void)
-{
- platform_device_unregister(omap3evm_snd_device);
-}
-
-module_init(omap3evm_soc_init);
-module_exit(omap3evm_soc_exit);
-
-MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * overo.c -- SoC audio for Gumstix Overo
- *
- * Author: Steve Sakoman <steve@sakoman.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int overo_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->codec_dai;
- int ret;
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops overo_ops = {
- .hw_params = overo_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link overo_dai = {
- .name = "TWL4030",
- .stream_name = "TWL4030",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &overo_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_card_overo = {
- .name = "overo",
- .owner = THIS_MODULE,
- .dai_link = &overo_dai,
- .num_links = 1,
-};
-
-static struct platform_device *overo_snd_device;
-
-static int __init overo_soc_init(void)
-{
- int ret;
-
- if (!(machine_is_overo() || machine_is_cm_t35())) {
- pr_debug("Incomatible machine!\n");
- return -ENODEV;
- }
- printk(KERN_INFO "overo SoC init\n");
-
- overo_snd_device = platform_device_alloc("soc-audio", -1);
- if (!overo_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(overo_snd_device, &snd_soc_card_overo);
-
- ret = platform_device_add(overo_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(overo_snd_device);
-
- return ret;
-}
-module_init(overo_soc_init);
-
-static void __exit overo_soc_exit(void)
-{
- platform_device_unregister(overo_snd_device);
-}
-module_exit(overo_soc_exit);
-
-MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
-MODULE_DESCRIPTION("ALSA SoC overo");
-MODULE_LICENSE("GPL");
BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0);
gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0);
- BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0);
- gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0);
-
return 0;
err1:
static void __exit zoom2_soc_exit(void)
{
gpio_free(ZOOM2_HEADSET_MUX_GPIO);
- gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO);
platform_device_unregister(zoom2_snd_device);
}
config SND_SOC_SAMSUNG
tristate "ASoC support for Samsung"
- depends on ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_EXYNOS4
+ depends on PLAT_SAMSUNG
select S3C64XX_DMA if ARCH_S3C64XX
select S3C2410_DMA if ARCH_S3C24XX
help
select SND_SAMSUNG_I2S
select SND_SOC_WM8996
select SND_SOC_WM9081
+ select SND_SOC_WM0010
select SND_SOC_WM1250_EV1
config SND_SOC_TOBERMORY
select SND_SAMSUNG_I2S
select SND_SOC_WM8962
+config SND_SOC_BELLS
+ tristate "Audio support for Wolfson Bells"
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ select SND_SAMSUNG_I2S
+ select SND_SOC_WM5102
+ select SND_SOC_WM5110
+ select SND_SOC_WM9081
+
config SND_SOC_LOWLAND
tristate "Audio support for Wolfson Lowland"
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
snd-soc-tobermory-objs := tobermory.o
snd-soc-lowland-objs := lowland.o
snd-soc-littlemill-objs := littlemill.o
+snd-soc-bells-objs := bells.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
+obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
--- /dev/null
+/*
+ * Bells audio support
+ *
+ * Copyright 2012 Wolfson Microelectronics
+ *
+ * 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 <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include "../codecs/wm5102.h"
+#include "../codecs/wm9081.h"
+
+/*
+ * 44.1kHz based clocks for the SYSCLK domain, use a very high clock
+ * to allow all the DSP functionality to be enabled if desired.
+ */
+#define SYSCLK_RATE (44100 * 1024)
+
+/* 48kHz based clocks for the ASYNC domain */
+#define ASYNCCLK_RATE (48000 * 512)
+
+/* BCLK2 is fixed at this currently */
+#define BCLK2_RATE (64 * 8000)
+
+/*
+ * Expect a 24.576MHz crystal if one is fitted (the driver will function
+ * if this is not fitted).
+ */
+#define MCLK_RATE 24576000
+
+#define WM9081_AUDIO_RATE 44100
+#define WM9081_MCLK_RATE (WM9081_AUDIO_RATE * 256)
+
+static int bells_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int ret;
+
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+ ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
+ ARIZONA_FLL_SRC_MCLK1,
+ MCLK_RATE,
+ SYSCLK_RATE);
+ if (ret < 0)
+ pr_err("Failed to start FLL: %d\n", ret);
+
+ ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
+ ARIZONA_FLL_SRC_AIF2BCLK,
+ BCLK2_RATE,
+ ASYNCCLK_RATE);
+ if (ret < 0)
+ pr_err("Failed to start FLL: %d\n", ret);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int bells_set_bias_level_post(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int ret;
+
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dapm->bias_level = level;
+
+ return 0;
+}
+
+static int bells_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_codec *codec = card->rtd[0].codec;
+ struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
+ struct snd_soc_dai *aif3_dai = card->rtd[2].cpu_dai;
+ struct snd_soc_dai *wm9081_dai = card->rtd[2].codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
+ if (ret != 0) {
+ dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
+ if (ret != 0) {
+ dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
+ if (ret != 0) {
+ dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
+ ARIZONA_CLK_SRC_FLL1, SYSCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
+ WM9081_MCLK_RATE, SND_SOC_CLOCK_OUT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
+ ARIZONA_CLK_SRC_FLL2, ASYNCCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
+ 0, WM9081_MCLK_RATE, 0);
+ if (ret != 0) {
+ dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_pcm_stream baseband_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static const struct snd_soc_pcm_stream sub_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = WM9081_AUDIO_RATE,
+ .rate_max = WM9081_AUDIO_RATE,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static struct snd_soc_dai_link bells_dai_wm5102[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm5102-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm5102-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ },
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm5102-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &baseband_params,
+ },
+ {
+ .name = "Sub",
+ .stream_name = "Sub",
+ .cpu_dai_name = "wm5102-aif3",
+ .codec_dai_name = "wm9081-hifi",
+ .codec_name = "wm9081.1-006c",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .params = &sub_params,
+ },
+};
+
+static struct snd_soc_dai_link bells_dai_wm5110[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm5110-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm5110-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ },
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm5110-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ .ignore_suspend = 1,
+ .params = &baseband_params,
+ },
+ {
+ .name = "Sub",
+ .stream_name = "Sub",
+ .cpu_dai_name = "wm5102-aif3",
+ .codec_dai_name = "wm9081-hifi",
+ .codec_name = "wm9081.1-006c",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .params = &sub_params,
+ },
+};
+
+static struct snd_soc_codec_conf bells_codec_conf[] = {
+ {
+ .dev_name = "wm9081.1-006c",
+ .name_prefix = "Sub",
+ },
+};
+
+static struct snd_soc_dapm_route bells_routes[] = {
+ { "Sub CLK_SYS", NULL, "OPCLK" },
+};
+
+static struct snd_soc_card bells_cards[] = {
+ {
+ .name = "Bells WM5102",
+ .owner = THIS_MODULE,
+ .dai_link = bells_dai_wm5102,
+ .num_links = ARRAY_SIZE(bells_dai_wm5102),
+ .codec_conf = bells_codec_conf,
+ .num_configs = ARRAY_SIZE(bells_codec_conf),
+
+ .late_probe = bells_late_probe,
+
+ .dapm_routes = bells_routes,
+ .num_dapm_routes = ARRAY_SIZE(bells_routes),
+
+ .set_bias_level = bells_set_bias_level,
+ .set_bias_level_post = bells_set_bias_level_post,
+ },
+ {
+ .name = "Bells WM5110",
+ .owner = THIS_MODULE,
+ .dai_link = bells_dai_wm5110,
+ .num_links = ARRAY_SIZE(bells_dai_wm5110),
+ .codec_conf = bells_codec_conf,
+ .num_configs = ARRAY_SIZE(bells_codec_conf),
+
+ .late_probe = bells_late_probe,
+
+ .dapm_routes = bells_routes,
+ .num_dapm_routes = ARRAY_SIZE(bells_routes),
+
+ .set_bias_level = bells_set_bias_level,
+ .set_bias_level_post = bells_set_bias_level_post,
+ },
+};
+
+
+static __devinit int bells_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ bells_cards[pdev->id].dev = &pdev->dev;
+
+ ret = snd_soc_register_card(&bells_cards[pdev->id]);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card(%s) failed: %d\n",
+ bells_cards[pdev->id].name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit bells_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&bells_cards[pdev->id]);
+
+ return 0;
+}
+
+static struct platform_driver bells_driver = {
+ .driver = {
+ .name = "bells",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = bells_probe,
+ .remove = __devexit_p(bells_remove),
+};
+
+module_platform_driver(bells_driver);
+
+MODULE_DESCRIPTION("Bells audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bells");
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
int ret;
if (dapm->dev != codec_dai->dev)
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
- struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
int ret;
if (dapm->dev != codec_dai->dev)
snd_soc_dapm_sync(&codec->dapm);
}
+static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(dai, 0, MCLK_AUDIO_RATE, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->codec_dai;
return 0;
}
+static const struct snd_soc_pcm_stream dsp_codec_params = {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
static struct snd_soc_dai_link speyside_dai[] = {
{
- .name = "CPU",
- .stream_name = "CPU",
+ .name = "CPU-DSP",
+ .stream_name = "CPU-DSP",
.cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8996-aif1",
+ .codec_dai_name = "wm0010-sdi1",
.platform_name = "samsung-audio",
+ .codec_name = "spi0.0",
+ .init = speyside_wm0010_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM,
+ },
+ {
+ .name = "DSP-CODEC",
+ .stream_name = "DSP-CODEC",
+ .cpu_dai_name = "wm0010-sdi2",
+ .codec_dai_name = "wm8996-aif1",
.codec_name = "wm8996.1-001a",
.init = speyside_wm8996_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM,
+ .params = &dsp_codec_params,
+ .ignore_suspend = 1,
},
{
.name = "Baseband",
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough FSI platform resources.\n");
- ret = -ENODEV;
- goto exit;
+ return -ENODEV;
}
- master = kzalloc(sizeof(*master), GFP_KERNEL);
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master) {
dev_err(&pdev->dev, "Could not allocate master\n");
- ret = -ENOMEM;
- goto exit;
+ return -ENOMEM;
}
- master->base = ioremap_nocache(res->start, resource_size(res));
+ master->base = devm_ioremap_nocache(&pdev->dev,
+ res->start, resource_size(res));
if (!master->base) {
- ret = -ENXIO;
dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
- goto exit_kfree;
+ return -ENXIO;
}
/* master setting */
ret = fsi_stream_probe(&master->fsia, &pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "FSIA stream probe failed\n");
- goto exit_iounmap;
+ return ret;
}
/* FSI B setting */
exit_free_irq:
free_irq(irq, master);
exit_fsib:
+ pm_runtime_disable(&pdev->dev);
fsi_stream_remove(&master->fsib);
exit_fsia:
fsi_stream_remove(&master->fsia);
-exit_iounmap:
- iounmap(master->base);
- pm_runtime_disable(&pdev->dev);
-exit_kfree:
- kfree(master);
- master = NULL;
-exit:
+
return ret;
}
fsi_stream_remove(&master->fsia);
fsi_stream_remove(&master->fsib);
- iounmap(master->base);
- kfree(master);
-
return 0;
}
--- /dev/null
+/*
+ * soc-compress.c -- ALSA SoC Compress
+ *
+ * Copyright (C) 2012 Intel Corp.
+ *
+ * Authors: Namarta Kohli <namartax.kohli@intel.com>
+ * Ramesh Babu K V <ramesh.babu@linux.intel.com>
+ * Vinod Koul <vinod.koul@linux.intel.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/compress_params.h>
+#include <sound/compress_driver.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+static int soc_compr_open(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
+ ret = platform->driver->compr_ops->open(cstream);
+ if (ret < 0) {
+ pr_err("compress asoc: can't open platform %s\n", platform->name);
+ goto out;
+ }
+ }
+
+ if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
+ ret = rtd->dai_link->compr_ops->startup(cstream);
+ if (ret < 0) {
+ pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name);
+ goto machine_err;
+ }
+ }
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ cpu_dai->playback_active++;
+ codec_dai->playback_active++;
+ } else {
+ cpu_dai->capture_active++;
+ codec_dai->capture_active++;
+ }
+
+ cpu_dai->active++;
+ codec_dai->active++;
+ rtd->codec->active++;
+
+ return 0;
+
+machine_err:
+ if (platform->driver->compr_ops && platform->driver->compr_ops->free)
+ platform->driver->compr_ops->free(cstream);
+out:
+ return ret;
+}
+
+static int soc_compr_free(struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ cpu_dai->playback_active--;
+ codec_dai->playback_active--;
+ } else {
+ cpu_dai->capture_active--;
+ codec_dai->capture_active--;
+ }
+
+ snd_soc_dai_digital_mute(codec_dai, 1);
+
+ cpu_dai->active--;
+ codec_dai->active--;
+ codec->active--;
+
+ if (!cpu_dai->active)
+ cpu_dai->rate = 0;
+
+ if (!codec_dai->active)
+ codec_dai->rate = 0;
+
+
+ if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
+ rtd->dai_link->compr_ops->shutdown(cstream);
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->free)
+ platform->driver->compr_ops->free(cstream);
+ cpu_dai->runtime = NULL;
+
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
+ rtd->dai_link->ignore_pmdown_time) {
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_STOP);
+ } else
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
+ } else {
+ /* capture streams can be powered down now */
+ snd_soc_dapm_stream_event(rtd,
+ SNDRV_PCM_STREAM_CAPTURE,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
+
+ return 0;
+}
+
+static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
+ ret = platform->driver->compr_ops->trigger(cstream, cmd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ snd_soc_dai_digital_mute(codec_dai, 0);
+ else if (cmd == SNDRV_PCM_TRIGGER_STOP)
+ snd_soc_dai_digital_mute(codec_dai, 1);
+
+ return ret;
+}
+
+static int soc_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ /* first we call set_params for the platform driver
+ * this should configure the soc side
+ * if the machine has compressed ops then we call that as well
+ * expectation is that platform and machine will configure everything
+ * for this compress path, like configuring pcm port for codec
+ */
+ if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
+ ret = platform->driver->compr_ops->set_params(cstream, params);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
+ ret = rtd->dai_link->compr_ops->set_params(cstream);
+ if (ret < 0)
+ return ret;
+ }
+
+ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_START);
+
+ return ret;
+}
+
+static int soc_compr_get_params(struct snd_compr_stream *cstream,
+ struct snd_codec *params)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
+ ret = platform->driver->compr_ops->get_params(cstream, params);
+
+ return ret;
+}
+
+static int soc_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
+ ret = platform->driver->compr_ops->get_caps(cstream, caps);
+
+ return ret;
+}
+
+static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
+ ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
+
+ return ret;
+}
+
+static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
+ ret = platform->driver->compr_ops->ack(cstream, bytes);
+
+ return ret;
+}
+
+static int soc_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
+ platform->driver->compr_ops->pointer(cstream, tstamp);
+
+ return 0;
+}
+
+/* ASoC Compress operations */
+static struct snd_compr_ops soc_compr_ops = {
+ .open = soc_compr_open,
+ .free = soc_compr_free,
+ .set_params = soc_compr_set_params,
+ .get_params = soc_compr_get_params,
+ .trigger = soc_compr_trigger,
+ .pointer = soc_compr_pointer,
+ .ack = soc_compr_ack,
+ .get_caps = soc_compr_get_caps,
+ .get_codec_caps = soc_compr_get_codec_caps
+};
+
+/* create a new compress */
+int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_compr *compr;
+ char new_name[64];
+ int ret = 0, direction = 0;
+
+ /* check client and interface hw capabilities */
+ snprintf(new_name, sizeof(new_name), "%s %s-%d",
+ rtd->dai_link->stream_name, codec_dai->name, num);
+ direction = SND_COMPRESS_PLAYBACK;
+ compr = kzalloc(sizeof(*compr), GFP_KERNEL);
+ if (compr == NULL) {
+ snd_printk(KERN_ERR "Cannot allocate compr\n");
+ return -ENOMEM;
+ }
+
+ compr->ops = &soc_compr_ops;
+ mutex_init(&compr->lock);
+ ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
+ if (ret < 0) {
+ pr_err("compress asoc: can't create compress for codec %s\n",
+ codec->name);
+ kfree(compr);
+ return ret;
+ }
+
+ rtd->compr = compr;
+ compr->private_data = rtd;
+
+ printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
+ cpu_dai->name);
+ return ret;
+}
SND_SOC_DAPM_STREAM_SUSPEND);
}
+ /* Recheck all analogue paths too */
+ dapm_mark_io_dirty(&card->dapm);
+ snd_soc_dapm_sync(&card->dapm);
+
/* suspend all CODECs */
list_for_each_entry(codec, &card->codec_dev_list, card_list) {
/* If there are paths active then the CODEC will be held with
codec->driver->suspend(codec);
codec->suspended = 1;
codec->cache_sync = 1;
+ if (codec->using_regmap)
+ regcache_mark_dirty(codec->control_data);
break;
default:
dev_dbg(codec->dev, "CODEC is on over suspend\n");
/* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
+
+ /* Recheck all analogue paths too */
+ dapm_mark_io_dirty(&card->dapm);
+ snd_soc_dapm_sync(&card->dapm);
}
/* powers up audio subsystem after a suspend */
if (ret < 0)
pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
- if (!dai_link->params) {
- /* create the pcm */
- ret = soc_new_pcm(rtd, num);
+ if (cpu_dai->driver->compress_dai) {
+ /*create compress_device"*/
+ ret = soc_new_compress(rtd, num);
if (ret < 0) {
- pr_err("asoc: can't create pcm %s :%d\n",
- dai_link->stream_name, ret);
+ pr_err("asoc: can't create compress %s\n",
+ dai_link->stream_name);
return ret;
}
} else {
- /* link the DAI widgets */
- play_w = codec_dai->playback_widget;
- capture_w = cpu_dai->capture_widget;
- if (play_w && capture_w) {
- ret = snd_soc_dapm_new_pcm(card, dai_link->params,
- capture_w, play_w);
- if (ret != 0) {
- dev_err(card->dev, "Can't link %s to %s: %d\n",
- play_w->name, capture_w->name, ret);
+
+ if (!dai_link->params) {
+ /* create the pcm */
+ ret = soc_new_pcm(rtd, num);
+ if (ret < 0) {
+ pr_err("asoc: can't create pcm %s :%d\n",
+ dai_link->stream_name, ret);
return ret;
}
- }
+ } else {
+ /* link the DAI widgets */
+ play_w = codec_dai->playback_widget;
+ capture_w = cpu_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ capture_w, play_w);
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
+ }
- play_w = cpu_dai->playback_widget;
- capture_w = codec_dai->capture_widget;
- if (play_w && capture_w) {
- ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+ play_w = cpu_dai->playback_widget;
+ capture_w = codec_dai->capture_widget;
+ if (play_w && capture_w) {
+ ret = snd_soc_dapm_new_pcm(card, dai_link->params,
capture_w, play_w);
- if (ret != 0) {
- dev_err(card->dev, "Can't link %s to %s: %d\n",
- play_w->name, capture_w->name, ret);
- return ret;
+ if (ret != 0) {
+ dev_err(card->dev, "Can't link %s to %s: %d\n",
+ play_w->name, capture_w->name, ret);
+ return ret;
+ }
}
}
}
static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- int ret = 0;
/*
* no card, so machine driver should be registering card
/* Bodge while we unpick instantiation */
card->dev = &pdev->dev;
- ret = snd_soc_register_card(card);
- if (ret != 0) {
- dev_err(&pdev->dev, "Failed to register card\n");
- return ret;
- }
-
- return 0;
+ return snd_soc_register_card(card);
}
static int soc_cleanup_card_resources(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, bitmask;
+ unsigned int val;
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
val = snd_soc_read(codec, e->reg);
ucontrol->value.enumerated.item[0]
- = (val >> e->shift_l) & (bitmask - 1);
+ = (val >> e->shift_l) & e->mask;
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
- (val >> e->shift_r) & (bitmask - 1);
+ (val >> e->shift_r) & e->mask;
return 0;
}
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
- unsigned int mask, bitmask;
+ unsigned int mask;
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
val = ucontrol->value.enumerated.item[0] << e->shift_l;
- mask = (bitmask - 1) << e->shift_l;
+ mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
- mask |= (bitmask - 1) << e->shift_r;
+ mask |= e->mask << e->shift_r;
}
return snd_soc_update_bits_locked(codec, e->reg, mask, val);
}
}
+ if (!dai->codec)
+ dai->dapm.idle_bias_off = 1;
+
list_add(&dai->list, &dai_list);
mutex_unlock(&client_mutex);
}
}
+ if (!dai->codec)
+ dai->dapm.idle_bias_off = 1;
+
list_add(&dai->list, &dai_list);
mutex_unlock(&client_mutex);
return 0;
fail:
- kfree(codec->reg_def_copy);
- codec->reg_def_copy = NULL;
kfree(codec->name);
kfree(codec);
return ret;
}
EXPORT_SYMBOL_GPL(dapm_mark_dirty);
+void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dapm_widget *w;
+
+ mutex_lock(&card->dapm_mutex);
+
+ list_for_each_entry(w, &card->widgets, list) {
+ switch (w->id) {
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ dapm_mark_dirty(w, "Rechecking inputs and outputs");
+ break;
+ default:
+ break;
+ }
+ }
+
+ mutex_unlock(&card->dapm_mutex);
+}
+EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
case snd_soc_dapm_mux: {
struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
- int val, item, bitmask;
+ int val, item;
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
val = soc_widget_read(w, e->reg);
- item = (val >> e->shift_l) & (bitmask - 1);
+ item = (val >> e->shift_l) & e->mask;
p->connect = 0;
for (i = 0; i < e->max; i++) {
int dapm_regulator_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- if (SND_SOC_DAPM_EVENT_ON(event))
+ int ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ ret = regulator_allow_bypass(w->regulator, true);
+ if (ret != 0)
+ dev_warn(w->dapm->dev,
+ "Failed to bypass %s: %d\n",
+ w->name, ret);
+ }
+
return regulator_enable(w->regulator);
- else
+ } else {
+ if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ ret = regulator_allow_bypass(w->regulator, false);
+ if (ret != 0)
+ dev_warn(w->dapm->dev,
+ "Failed to unbypass %s: %d\n",
+ w->name, ret);
+ }
+
return regulator_disable_deferred(w->regulator, w->shift);
+ }
}
EXPORT_SYMBOL_GPL(dapm_regulator_event);
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, bitmask;
+ unsigned int val;
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
val = snd_soc_read(widget->codec, e->reg);
- ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask;
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
- (val >> e->shift_r) & (bitmask - 1);
+ (val >> e->shift_r) & e->mask;
return 0;
}
struct snd_soc_card *card = codec->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
- unsigned int mask, bitmask;
+ unsigned int mask;
struct snd_soc_dapm_update update;
int wi;
- for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
- ;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
mux = ucontrol->value.enumerated.item[0];
val = mux << e->shift_l;
- mask = (bitmask - 1) << e->shift_l;
+ mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
- mask |= (bitmask - 1) << e->shift_r;
+ mask |= e->mask << e->shift_r;
}
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
struct dma_chan *chan = prtd->dma_chan;
struct dma_async_tx_descriptor *desc;
enum dma_transfer_direction direction;
+ unsigned long flags = DMA_CTRL_ACK;
direction = snd_pcm_substream_to_dma_direction(substream);
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
prtd->pos = 0;
desc = dmaengine_prep_dma_cyclic(chan,
substream->runtime->dma_addr,
snd_pcm_lib_buffer_bytes(substream),
- snd_pcm_lib_period_bytes(substream), direction);
+ snd_pcm_lib_period_bytes(substream), direction, flags);
if (!desc)
return -ENOMEM;
jack->status &= ~mask;
jack->status |= status & mask;
- /* The DAPM sync is expensive enough to be worth skipping.
- * However, empty mask means pin synchronization is desired. */
- if (mask && (jack->status == oldstatus))
- goto out;
-
trace_snd_soc_jack_notify(jack, status);
list_for_each_entry(pin, &jack->pins, list) {
snd_jack_report(jack->jack, jack->status);
-out:
mutex_unlock(&jack->mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_report);
#include <linux/gpio.h>
#include <linux/of_gpio.h>
-#include <mach/tegra_wm8903_pdata.h>
-
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/tegra_wm8903.h>
#include "../codecs/wm8903.h"
drvdata = devm_kzalloc(&pdev->dev,
sizeof(struct ux500_msp_i2s_drvdata),
GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
drvdata->fmt = 0;
drvdata->slots = 1;
drvdata->tx_mask = 0x01;
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
msp = *msp_p;
+ if (!msp)
+ return -ENOMEM;
if (np) {
if (!platform_data) {
default:
swval = &amd->pgain;
break;
- };
+ }
ucontrol->value.integer.value[0] = *swval;
default:
swval = &amd->pgain;
break;
- };
+ }
spin_lock_irqsave(&amd->lock, flags);
break;
default:
printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n");
- };
+ }
return b;
}
if (!memcmp(version, known_fw_versions + i, 4))
return 0;
- snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
- "%02x %02x %02x %02x. "
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. "
"please reconnect to power. if this failure "
"still happens, check your firmware installation.",
- version[0], version[1], version[2], version[3]);
+ 4, version);
return -EINVAL;
}
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, struct snd_usb_stream, list);
snd_pcm_suspend_all(as->pcm);
+ as->substream[0].need_setup_ep =
+ as->substream[1].need_setup_ep = true;
}
}
} else {
unsigned char silence_value;
unsigned int stride;
int iface, alt_idx;
+ int skip_packets; /* quirks for devices to ignore the first n packets
+ in a stream */
spinlock_t lock;
struct list_head list;
int interface; /* current interface */
int endpoint; /* assigned endpoint */
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
+ snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */
+ unsigned int channels; /* current number of channels (for hw_params callback) */
unsigned int cur_rate; /* current rate (for hw_params callback) */
unsigned int period_bytes; /* current period bytes (for hw_params callback) */
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int hwptr_done; /* processed byte position in the buffer */
unsigned int transfer_done; /* processed frames since last period update */
- unsigned long active_mask; /* bitmask of active urbs */
- unsigned long unlink_mask; /* bitmask of unlinked urbs */
/* data and sync endpoints for this stream */
unsigned int ep_num; /* the endpoint number */
struct snd_usb_endpoint *data_endpoint;
struct snd_usb_endpoint *sync_endpoint;
unsigned long flags;
+ bool need_setup_ep; /* (re)configure EP at prepare? */
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
#include "card.h"
#include "endpoint.h"
#include "pcm.h"
+#include "quirks.h"
#define EP_FLAG_ACTIVATED 0
#define EP_FLAG_RUNNING 1
{
struct urb *urb = urb_ctx->urb;
+ if (unlikely(ep->skip_packets > 0)) {
+ ep->skip_packets--;
+ return;
+ }
+
if (ep->sync_slave)
snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
* configure a data endpoint
*/
static int data_ep_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep)
{
unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
- int period_bytes = params_period_bytes(hw_params);
- int format = params_format(hw_params);
int is_playback = usb_pipeout(ep->pipe);
- int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
- params_channels(hw_params);
+ int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
ep->datainterval = fmt->datainterval;
ep->stride = frame_bits >> 3;
- ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+ ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
/* calculate max. frequency */
if (ep->maxpacksize) {
* configure a sync endpoint
*/
static int sync_ep_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
struct audioformat *fmt)
{
int i;
* snd_usb_endpoint_set_params: configure an snd_usb_endpoint
*
* @ep: the snd_usb_endpoint to configure
- * @hw_params: the hardware parameters
+ * @pcm_format: the audio fomat.
+ * @channels: the number of audio channels.
+ * @period_bytes: the number of bytes in one alsa period.
+ * @rate: the frame rate.
* @fmt: the USB audio format information
* @sync_ep: the sync endpoint to use, if any
*
* An endpoint that is already running can not be reconfigured.
*/
int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int rate,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep)
{
ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
- ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+ ep->freqn = get_usb_full_speed_rate(rate);
else
- ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+ ep->freqn = get_usb_high_speed_rate(rate);
/* calculate the frequency in 16.16 format */
ep->freqm = ep->freqn;
switch (ep->type) {
case SND_USB_ENDPOINT_TYPE_DATA:
- err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+ err = data_ep_set_params(ep, pcm_format, channels,
+ period_bytes, fmt, sync_ep);
break;
case SND_USB_ENDPOINT_TYPE_SYNC:
- err = sync_ep_set_params(ep, hw_params, fmt);
+ err = sync_ep_set_params(ep, fmt);
break;
default:
err = -EINVAL;
ep->unlink_mask = 0;
ep->phase = 0;
+ snd_usb_endpoint_start_quirk(ep);
+
/*
* If this endpoint has a data endpoint as implicit feedback source,
* don't start the urbs here. Instead, mark them all as available,
int ep_num, int direction, int type);
int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
- struct snd_pcm_hw_params *hw_params,
+ snd_pcm_format_t pcm_format,
+ unsigned int channels,
+ unsigned int period_bytes,
+ unsigned int rate,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
#include "usbaudio.h"
#include "helper.h"
+#include "quirks.h"
/*
* combine bytes and get an integer value
memcpy(data, buf, size);
kfree(buf);
}
+
+ snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
+ value, index, data, size);
+
return err;
}
/* disable non-functional volume control */
master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME);
break;
+ case USB_ID(0x1130, 0xf211):
+ snd_printk(KERN_INFO
+ "usbmixer: volume control quirk for Tenx TP6911 Audio Headset\n");
+ /* disable non-functional volume control */
+ channels = 0;
+ break;
+
}
if (channels > 0)
first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
/*
* find a matching audio format
*/
-static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
- unsigned int rate, unsigned int channels)
+static struct audioformat *find_format(struct snd_usb_substream *subs)
{
struct list_head *p;
struct audioformat *found = NULL;
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
- if (!(fp->formats & (1uLL << format)))
+ if (!(fp->formats & (1uLL << subs->pcm_format)))
continue;
- if (fp->channels != channels)
+ if (fp->channels != subs->channels)
continue;
- if (rate < fp->rate_min || rate > fp->rate_max)
+ if (subs->cur_rate < fp->rate_min ||
+ subs->cur_rate > fp->rate_max)
continue;
if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
unsigned int i;
for (i = 0; i < fp->nr_rates; i++)
- if (fp->rate_table[i] == rate)
+ if (fp->rate_table[i] == subs->cur_rate)
break;
if (i >= fp->nr_rates)
continue;
return 0;
}
+/*
+ * configure endpoint params
+ *
+ * called during initial setup and upon resume
+ */
+static int configure_endpoint(struct snd_usb_substream *subs)
+{
+ int ret;
+
+ mutex_lock(&subs->stream->chip->shutdown_mutex);
+ /* format changed */
+ stop_endpoints(subs, 0, 0, 0);
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ subs->sync_endpoint);
+ if (ret < 0)
+ goto unlock;
+
+ if (subs->sync_endpoint)
+ ret = snd_usb_endpoint_set_params(subs->data_endpoint,
+ subs->pcm_format,
+ subs->channels,
+ subs->period_bytes,
+ subs->cur_rate,
+ subs->cur_audiofmt,
+ NULL);
+
+unlock:
+ mutex_unlock(&subs->stream->chip->shutdown_mutex);
+ return ret;
+}
+
/*
* hw_params callback
*
{
struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
- unsigned int channels, rate, format;
- int ret, changed;
+ int ret;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
- format = params_format(hw_params);
- rate = params_rate(hw_params);
- channels = params_channels(hw_params);
- fmt = find_format(subs, format, rate, channels);
+ subs->pcm_format = params_format(hw_params);
+ subs->period_bytes = params_period_bytes(hw_params);
+ subs->channels = params_channels(hw_params);
+ subs->cur_rate = params_rate(hw_params);
+
+ fmt = find_format(subs);
if (!fmt) {
snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
- format, rate, channels);
+ subs->pcm_format, subs->cur_rate, subs->channels);
return -EINVAL;
}
- changed = subs->cur_audiofmt != fmt ||
- subs->period_bytes != params_period_bytes(hw_params) ||
- subs->cur_rate != rate;
if ((ret = set_format(subs, fmt)) < 0)
return ret;
- if (subs->cur_rate != rate) {
- struct usb_host_interface *alts;
- struct usb_interface *iface;
- iface = usb_ifnum_to_if(subs->dev, fmt->iface);
- alts = &iface->altsetting[fmt->altset_idx];
- ret = snd_usb_init_sample_rate(subs->stream->chip, fmt->iface, alts, fmt, rate);
- if (ret < 0)
- return ret;
- subs->cur_rate = rate;
- }
-
- if (changed) {
- mutex_lock(&subs->stream->chip->shutdown_mutex);
- /* format changed */
- stop_endpoints(subs, 0, 0, 0);
- ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
- subs->sync_endpoint);
- if (ret < 0)
- goto unlock;
+ subs->interface = fmt->iface;
+ subs->altset_idx = fmt->altset_idx;
+ subs->need_setup_ep = true;
- if (subs->sync_endpoint)
- ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
- hw_params, fmt, NULL);
-unlock:
- mutex_unlock(&subs->stream->chip->shutdown_mutex);
- }
-
- if (ret == 0) {
- subs->interface = fmt->iface;
- subs->altset_idx = fmt->altset_idx;
- }
-
- return ret;
+ return 0;
}
/*
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usb_substream *subs = runtime->private_data;
+ struct usb_host_interface *alts;
+ struct usb_interface *iface;
+ int ret;
if (! subs->cur_audiofmt) {
snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
if (snd_BUG_ON(!subs->data_endpoint))
return -EIO;
+ ret = set_format(subs, subs->cur_audiofmt);
+ if (ret < 0)
+ return ret;
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ ret = snd_usb_init_sample_rate(subs->stream->chip,
+ subs->cur_audiofmt->iface,
+ alts,
+ subs->cur_audiofmt,
+ subs->cur_rate);
+ if (ret < 0)
+ return ret;
+
+ if (subs->need_setup_ep) {
+ ret = configure_endpoint(subs);
+ if (ret < 0)
+ return ret;
+ subs->need_setup_ep = false;
+ }
+
/* some unit conversions in runtime */
subs->data_endpoint->maxframesize =
bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
}
},
+/* Microsoft XboxLive Headset/Xbox Communicator */
+{
+ USB_DEVICE(0x045e, 0x0283),
+ .bInterfaceClass = USB_CLASS_PER_INTERFACE,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Microsoft",
+ .product_name = "XboxLive Headset/Xbox Communicator",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = &(const struct snd_usb_audio_quirk[]) {
+ {
+ /* playback */
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 0,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x04,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 22050,
+ .rate_max = 22050
+ }
+ },
+ {
+ /* capture */
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = &(const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels = 1,
+ .iface = 1,
+ .altsetting = 0,
+ .altset_idx = 0,
+ .attributes = 0,
+ .endpoint = 0x85,
+ .ep_attr = 0x05,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 16000,
+ .rate_max = 16000
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+
{
/*
* Some USB MIDI devices don't have an audio control interface,
}
}
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
+{
+ /*
+ * "Playback Design" products send bogus feedback data at the start
+ * of the stream. Ignore them.
+ */
+ if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+ ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
+ ep->skip_packets = 4;
+}
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size)
+{
+ /*
+ * "Playback Design" products need a 20ms delay after each
+ * class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ mdelay(20);
+}
+
#ifndef __USBAUDIO_QUIRKS_H
#define __USBAUDIO_QUIRKS_H
+struct audioformat;
+struct snd_usb_endpoint;
+struct snd_usb_substream;
+
int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
struct usb_driver *driver,
int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
struct audioformat *fp);
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep);
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value,
+ __u16 index, void *data, __u16 size);
+
#endif /* __USBAUDIO_QUIRKS_H */