<para>
The macro takes an conditional expression to evaluate.
- When <constant>CONFIG_SND_DEBUG</constant>, is set, the
- expression is actually evaluated. If it's non-zero, it shows
- the warning message such as
+ When <constant>CONFIG_SND_DEBUG</constant>, is set, if the
+ expression is non-zero, it shows the warning message such as
<computeroutput>BUG? (xxx)</computeroutput>
- normally followed by stack trace. It returns the evaluated
- value.
- When no <constant>CONFIG_SND_DEBUG</constant> is set, this
- macro always returns zero.
+ normally followed by stack trace.
+
+ In both cases it returns the evaluated value.
</para>
</section>
--- /dev/null
+AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC
+
+This device has no control interface.
+
+Required properties:
+
+ - compatible : "asahi-kasei,ak5386"
+
+Optional properties:
+
+ - reset-gpio : a GPIO spec for the reset/power down pin.
+ If specified, it will be deasserted at probe time.
+
+Example:
+
+spdif: ak5386@0 {
+ compatible = "asahi-kasei,ak5386";
+ reset-gpio = <&gpio0 23>;
+};
NVIDIA Tegra30 AHUB (Audio Hub)
Required properties:
-- compatible : "nvidia,tegra30-ahub"
+- compatible : "nvidia,tegra30-ahub", "nvidia,tegra114-ahub", etc.
- reg : Should contain the register physical address and length for each of
- the AHUB's APBIF registers and the AHUB's own registers.
+ the AHUB's register blocks.
+ - Tegra30 requires 2 entries, for the APBIF and AHUB/AUDIO register blocks.
+ - Tegra114 requires an additional entry, for the APBIF2 register block.
- interrupts : Should contain AHUB interrupt
-- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
- request selector for the first APBIF channel.
+- nvidia,dma-request-selector : A list of the DMA channel specifiers. Each
+ entry contains the Tegra DMA controller's phandle and request selector.
+ If a single entry is present, the request selectors for the channels are
+ assumed to be contiguous, and increment from this value.
+ If multiple values are given, one value must be given per channel.
+- clocks : Must contain an entry for each required entry in clock-names.
+- clock-names : Must include the following entries:
+ - Tegra30: Requires d_audio, apbif, i2s0, i2s1, i2s2, i2s3, i2s4, dam0,
+ dam1, dam2, spdif_in.
+ - Tegra114: Additionally requires amx, adx.
- ranges : The bus address mapping for the configlink register bus.
Can be empty since the mapping is 1:1.
- #address-cells : For the configlink bus. Should be <1>;
reg = <0x70080000 0x200 0x70080200 0x100>;
interrupts = < 0 103 0x04 >;
nvidia,dma-request-selector = <&apbdma 1>;
-
+ clocks = <&tegra_car 106>, <&tegra_car 107>, <&tegra_car 30>,
+ <&tegra_car 11>, <&tegra_car 18>, <&tegra_car 101>,
+ <&tegra_car 102>, <&tegra_car 108>, <&tegra_car 109>,
+ <&tegra_car 110>, <&tegra_car 162>;
+ clock-names = "d_audio", "apbif", "i2s0", "i2s1", "i2s2",
+ "i2s3", "i2s4", "dam0", "dam1", "dam2",
+ "spdif_in";
ranges;
#address-cells = <1>;
#size-cells = <1>;
--- /dev/null
+Texas Instruments TAS5086 6-channel PWM Processor
+
+Required properties:
+
+ - compatible: Should contain "ti,tas5086".
+ - reg: The i2c address. Should contain <0x1b>.
+
+Optional properties:
+
+ - reset-gpio: A GPIO spec to define which pin is connected to the
+ chip's !RESET pin. If specified, the driver will
+ assert a hardware reset at probe time.
+
+ - ti,charge-period: This property should contain the time in microseconds
+ that closely matches the external single-ended
+ split-capacitor charge period. The hardware chip
+ waits for this period of time before starting the
+ PWM signals. This helps reduce pops and clicks.
+
+ When not specified, the hardware default of 1300ms
+ is retained.
+
+Examples:
+
+ i2c_bus {
+ tas5086@1b {
+ compatible = "ti,tas5086";
+ reg = <0x1b>;
+ reset-gpio = <&gpio 23 0>;
+ ti,charge-period = <156000>;
+ };
+ };
the corresponding mixer control, if available
- add_stereo_mix_input (bool): add the stereo mix (analog-loopback
mix) to the input mux if available
-- add_out_jack_modes (bool): add "xxx Jack Mode" enum controls to each
- output jack for allowing to change the headphone amp capability
-- add_in_jack_modes (bool): add "xxx Jack Mode" enum controls to each
- input jack for allowing to change the mic bias vref
+- add_jack_modes (bool): add "xxx Jack Mode" enum controls to each
+ I/O jack for allowing to change the headphone amp and mic bias VREF
+ capabilities
- power_down_unused (bool): power down the unused widgets
+- add_hp_mic (bool): add the headphone to capture source if possible
+- hp_mic_detect (bool): enable/disable the hp/mic shared input for a
+ single built-in mic case; default true
- mixer_nid (int): specifies the widget NID of the analog-loopback
mixer
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-ac97.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-ac97.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID }
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-ac97.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <plat/regs-ac97.h>
#include <plat/regs-dma.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { \
/* ASOC DMA */
+#ifdef CONFIG_PLAT_S5P
+static struct resource samsung_asoc_idma_resource = DEFINE_RES_IRQ(IRQ_I2S0);
+
struct platform_device samsung_asoc_idma = {
.name = "samsung-idma",
.id = -1,
+ .num_resources = 1,
+ .resource = &samsung_asoc_idma_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
+#endif
/* FB */
NULL,
};
+static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
+ unsigned int magic)
+{
+ struct arizona *arizona = info->arizona;
+ int ret;
+
+ mutex_lock(&arizona->dapm->card->dapm_mutex);
+
+ arizona->hpdet_magic = magic;
+
+ /* Keep the HP output stages disabled while doing the magic */
+ if (magic) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, 0);
+ if (ret != 0)
+ dev_warn(arizona->dev,
+ "Failed to disable headphone outputs: %d\n",
+ ret);
+ }
+
+ ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
+ magic);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to do magic: %d\n",
+ ret);
+
+ ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
+ magic);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to do magic: %d\n",
+ ret);
+
+ /* Restore the desired state while not doing the magic */
+ if (!magic) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, arizona->hp_ena);
+ if (ret != 0)
+ dev_warn(arizona->dev,
+ "Failed to restore headphone outputs: %d\n",
+ ret);
+ }
+
+ mutex_unlock(&arizona->dapm->card->dapm_mutex);
+}
+
static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
{
struct arizona *arizona = info->arizona;
struct arizona *arizona = info->arizona;
int id_gpio = arizona->pdata.hpdet_id_gpio;
int report = ARIZONA_CABLE_HEADPHONE;
- unsigned int val;
int ret, reading;
mutex_lock(&info->lock);
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret);
- mutex_lock(&arizona->dapm->card->dapm_mutex);
-
- ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read output enables: %d\n",
- ret);
- val = 0;
- }
-
- if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) {
- ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to undo magic: %d\n",
- ret);
-
- ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to undo magic: %d\n",
- ret);
- }
-
- mutex_unlock(&arizona->dapm->card->dapm_mutex);
+ arizona_extcon_do_magic(info, 0);
done:
if (id_gpio)
if (info->mic)
arizona_stop_mic(info);
- ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
-
- ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
+ arizona_extcon_do_magic(info, 0x4000);
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
{
struct arizona *arizona = info->arizona;
- unsigned int val;
int ret;
dev_dbg(arizona->dev, "Starting identification via HPDET\n");
arizona_extcon_pulse_micbias(info);
- mutex_lock(&arizona->dapm->card->dapm_mutex);
-
- ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to read output enables: %d\n",
- ret);
- val = 0;
- }
-
- if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) {
- ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
- 0x4000);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do magic: %d\n",
- ret);
-
- ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
- 0x4000);
- if (ret != 0)
- dev_warn(arizona->dev, "Failed to do magic: %d\n",
- ret);
- }
-
- mutex_unlock(&arizona->dapm->card->dapm_mutex);
+ arizona_extcon_do_magic(info, 0x4000);
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ACCESSORY_DETECT_MODE_1,
{ 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
{ 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */
{ 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
+ { 0x00000179, 0x0000 }, /* R377 - FLL1 Control 7 */
{ 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
{ 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
{ 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
{ 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
{ 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
{ 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000187, 0x0001 }, /* R391 - FLL1 Synchroniser 7 */
{ 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
{ 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */
{ 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */
{ 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
{ 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
{ 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
+ { 0x00000199, 0x0000 }, /* R409 - FLL2 Control 7 */
{ 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
{ 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
{ 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
{ 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
{ 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
{ 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A7, 0x0001 }, /* R423 - FLL2 Synchroniser 7 */
{ 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
{ 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
case ARIZONA_FLL1_CONTROL_6:
case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL1_CONTROL_7:
case ARIZONA_FLL1_SYNCHRONISER_1:
case ARIZONA_FLL1_SYNCHRONISER_2:
case ARIZONA_FLL1_SYNCHRONISER_3:
case ARIZONA_FLL1_SYNCHRONISER_4:
case ARIZONA_FLL1_SYNCHRONISER_5:
case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SYNCHRONISER_7:
case ARIZONA_FLL1_SPREAD_SPECTRUM:
case ARIZONA_FLL1_GPIO_CLOCK:
case ARIZONA_FLL2_CONTROL_1:
case ARIZONA_FLL2_CONTROL_6:
case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
case ARIZONA_FLL2_NCO_TEST_0:
+ case ARIZONA_FLL2_CONTROL_7:
case ARIZONA_FLL2_SYNCHRONISER_1:
case ARIZONA_FLL2_SYNCHRONISER_2:
case ARIZONA_FLL2_SYNCHRONISER_3:
case ARIZONA_FLL2_SYNCHRONISER_4:
case ARIZONA_FLL2_SYNCHRONISER_5:
case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SYNCHRONISER_7:
case ARIZONA_FLL2_SPREAD_SPECTRUM:
case ARIZONA_FLL2_GPIO_CLOCK:
case ARIZONA_MIC_CHARGE_PUMP_1:
case ARIZONA_NOISE_GATE_CONTROL:
case ARIZONA_PDM_SPK1_CTRL_1:
case ARIZONA_PDM_SPK1_CTRL_2:
+ case ARIZONA_SPK_CTRL_2:
+ case ARIZONA_SPK_CTRL_3:
case ARIZONA_DAC_COMP_1:
case ARIZONA_DAC_COMP_2:
case ARIZONA_DAC_COMP_3:
struct regmap_irq_chip_data *aod_irq_chip;
struct regmap_irq_chip_data *irq_chip;
+ bool hpdet_magic;
+ unsigned int hp_ena;
+
struct mutex clk_lock;
int clk32k_ref;
#define ARIZONA_FLL1_CONTROL_6 0x176
#define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177
#define ARIZONA_FLL1_NCO_TEST_0 0x178
+#define ARIZONA_FLL1_CONTROL_7 0x179
#define ARIZONA_FLL1_SYNCHRONISER_1 0x181
#define ARIZONA_FLL1_SYNCHRONISER_2 0x182
#define ARIZONA_FLL1_SYNCHRONISER_3 0x183
#define ARIZONA_FLL1_SYNCHRONISER_4 0x184
#define ARIZONA_FLL1_SYNCHRONISER_5 0x185
#define ARIZONA_FLL1_SYNCHRONISER_6 0x186
+#define ARIZONA_FLL1_SYNCHRONISER_7 0x187
#define ARIZONA_FLL1_SPREAD_SPECTRUM 0x189
#define ARIZONA_FLL1_GPIO_CLOCK 0x18A
#define ARIZONA_FLL2_CONTROL_1 0x191
#define ARIZONA_FLL2_CONTROL_6 0x196
#define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197
#define ARIZONA_FLL2_NCO_TEST_0 0x198
+#define ARIZONA_FLL2_CONTROL_7 0x199
#define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1
#define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2
#define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3
#define ARIZONA_FLL2_SYNCHRONISER_4 0x1A4
#define ARIZONA_FLL2_SYNCHRONISER_5 0x1A5
#define ARIZONA_FLL2_SYNCHRONISER_6 0x1A6
+#define ARIZONA_FLL2_SYNCHRONISER_7 0x1A7
#define ARIZONA_FLL2_SPREAD_SPECTRUM 0x1A9
#define ARIZONA_FLL2_GPIO_CLOCK 0x1AA
#define ARIZONA_MIC_CHARGE_PUMP_1 0x200
#define ARIZONA_PDM_SPK1_CTRL_2 0x491
#define ARIZONA_PDM_SPK2_CTRL_1 0x492
#define ARIZONA_PDM_SPK2_CTRL_2 0x493
+#define ARIZONA_SPK_CTRL_2 0x4B5
+#define ARIZONA_SPK_CTRL_3 0x4B6
#define ARIZONA_DAC_COMP_1 0x4DC
#define ARIZONA_DAC_COMP_2 0x4DD
#define ARIZONA_DAC_COMP_3 0x4DE
#define ARIZONA_FLL1_FRC_INTEG_VAL_SHIFT 0 /* FLL1_FRC_INTEG_VAL - [11:0] */
#define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH 12 /* FLL1_FRC_INTEG_VAL - [11:0] */
+/*
+ * R377 (0x179) - FLL1 Control 7
+ */
+#define ARIZONA_FLL1_GAIN_MASK 0x003c /* FLL1_GAIN */
+#define ARIZONA_FLL1_GAIN_SHIFT 2 /* FLL1_GAIN */
+#define ARIZONA_FLL1_GAIN_WIDTH 4 /* FLL1_GAIN */
+
/*
* R385 (0x181) - FLL1 Synchroniser 1
*/
#define ARIZONA_FLL1_CLK_SYNC_SRC_SHIFT 0 /* FLL1_CLK_SYNC_SRC - [3:0] */
#define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH 4 /* FLL1_CLK_SYNC_SRC - [3:0] */
+/*
+ * R391 (0x187) - FLL1 Synchroniser 7
+ */
+#define ARIZONA_FLL1_SYNC_GAIN_MASK 0x003c /* FLL1_SYNC_GAIN */
+#define ARIZONA_FLL1_SYNC_GAIN_SHIFT 2 /* FLL1_SYNC_GAIN */
+#define ARIZONA_FLL1_SYNC_GAIN_WIDTH 4 /* FLL1_SYNC_GAIN */
+#define ARIZONA_FLL1_SYNC_BW 0x0001 /* FLL1_SYNC_BW */
+#define ARIZONA_FLL1_SYNC_BW_MASK 0x0001 /* FLL1_SYNC_BW */
+#define ARIZONA_FLL1_SYNC_BW_SHIFT 0 /* FLL1_SYNC_BW */
+#define ARIZONA_FLL1_SYNC_BW_WIDTH 1 /* FLL1_SYNC_BW */
+
/*
* R393 (0x189) - FLL1 Spread Spectrum
*/
#define ARIZONA_FLL2_FRC_INTEG_VAL_SHIFT 0 /* FLL2_FRC_INTEG_VAL - [11:0] */
#define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH 12 /* FLL2_FRC_INTEG_VAL - [11:0] */
+/*
+ * R409 (0x199) - FLL2 Control 7
+ */
+#define ARIZONA_FLL2_GAIN_MASK 0x003c /* FLL2_GAIN */
+#define ARIZONA_FLL2_GAIN_SHIFT 2 /* FLL2_GAIN */
+#define ARIZONA_FLL2_GAIN_WIDTH 4 /* FLL2_GAIN */
+
/*
* R417 (0x1A1) - FLL2 Synchroniser 1
*/
#define ARIZONA_FLL2_CLK_SYNC_SRC_SHIFT 0 /* FLL2_CLK_SYNC_SRC - [3:0] */
#define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH 4 /* FLL2_CLK_SYNC_SRC - [3:0] */
+/*
+ * R423 (0x1A7) - FLL2 Synchroniser 7
+ */
+#define ARIZONA_FLL2_SYNC_GAIN_MASK 0x003c /* FLL2_SYNC_GAIN */
+#define ARIZONA_FLL2_SYNC_GAIN_SHIFT 2 /* FLL2_SYNC_GAIN */
+#define ARIZONA_FLL2_SYNC_GAIN_WIDTH 4 /* FLL2_SYNC_GAIN */
+#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */
+#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */
+#define ARIZONA_FLL2_SYNC_BW_SHIFT 0 /* FLL2_SYNC_BW */
+#define ARIZONA_FLL2_SYNC_BW_WIDTH 1 /* FLL2_SYNC_BW */
+
/*
* R425 (0x1A9) - FLL2 Spread Spectrum
*/
#define WM8994_NUM_LDO 2
#define WM8994_NUM_GPIO 11
+#define WM8994_NUM_AIF 3
struct wm8994_ldo_pdata {
/** GPIOs to enable regulator, 0 or less if not available */
* system.
*/
bool spkmode_pu;
+
+ /**
+ * Maximum number of channels clocks will be generated for,
+ * useful for systems where and I2S bus with multiple data
+ * lines is mastered.
+ */
+ int max_channels_clocked[WM8994_NUM_AIF];
};
#endif
__u8 iChannelNames;
} __attribute__((packed));
+#define UAC2_FORMAT_TYPE_I_RAW_DATA (1 << 31)
+
/* 4.10.1.2 Class-Specific AS Isochronous Audio Data Endpoint Descriptor */
struct uac2_iso_endpoint_descriptor {
*
* Add a virtual slave control to the given master element created via
* snd_ctl_create_virtual_master() beforehand.
- * Returns zero if successful or a negative error code.
*
* All slaves must be the same type (returning the same information
* via info callback). The function doesn't check it, so it's your
* at most two channels,
* logarithmic volume control (dB level) thus no linear volume,
* master can only attenuate the volume without gain
+ *
+ * Return: Zero if successful or a negative error code.
*/
static inline int
snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
* When the control peeks the hardware values directly and the value
* can be changed by other means than the put callback of the element,
* this function should be used to keep the value always up-to-date.
+ *
+ * Return: Zero if successful or a negative error code.
*/
static inline int
snd_ctl_add_slave_uncached(struct snd_kcontrol *master,
* This function uses the card's device pointer to link to the
* correct &struct device.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
static inline int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
* snd_BUG_ON - debugging check macro
* @cond: condition to evaluate
*
- * When CONFIG_SND_DEBUG is set, this macro evaluates the given condition,
- * and call WARN() and returns the value if it's non-zero.
- *
- * When CONFIG_SND_DEBUG is not set, this just returns zero, and the given
- * condition is ignored.
- *
- * NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n.
- * Thus, don't put any statement that influences on the code behavior,
- * such as pre/post increment, to the argument of this macro.
- * If you want to evaluate and give a warning, use standard WARN_ON().
+ * Has the same behavior as WARN_ON when CONFIG_SND_DEBUG is set,
+ * otherwise just evaluates the conditional and returns the value.
*/
-#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond))
+#define snd_BUG_ON(cond) WARN_ON((cond))
#else /* !CONFIG_SND_DEBUG */
static inline void _snd_printd(int level, const char *format, ...) {}
#define snd_BUG() do { } while (0)
-static inline int __snd_bug_on(int cond)
-{
- return 0;
-}
-#define snd_BUG_ON(cond) __snd_bug_on(0 && (cond)) /* always false */
+
+#define snd_BUG_ON(condition) ({ \
+ int __ret_warn_on = !!(condition); \
+ unlikely(__ret_warn_on); \
+})
#endif /* CONFIG_SND_DEBUG */
#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B)
#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40)
#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B)
+#define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8)
+#define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE)
#ifdef SNDRV_LITTLE_ENDIAN
#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE
*
* Checks whether enough free space is available on the playback buffer.
*
- * Returns non-zero if available, or zero if not.
+ * Return: Non-zero if available, or zero if not.
*/
static inline int snd_pcm_playback_ready(struct snd_pcm_substream *substream)
{
*
* Checks whether enough capture data is available on the capture buffer.
*
- * Returns non-zero if available, or zero if not.
+ * Return: Non-zero if available, or zero if not.
*/
static inline int snd_pcm_capture_ready(struct snd_pcm_substream *substream)
{
* snd_pcm_playback_data - check whether any data exists on the playback buffer
* @substream: the pcm substream instance
*
- * Checks whether any data exists on the playback buffer. If stop_threshold
- * is bigger or equal to boundary, then this function returns always non-zero.
+ * Checks whether any data exists on the playback buffer.
*
- * Returns non-zero if exists, or zero if not.
+ * Return: Non-zero if any data exists, or zero if not. If stop_threshold
+ * is bigger or equal to boundary, then this function returns always non-zero.
*/
static inline int snd_pcm_playback_data(struct snd_pcm_substream *substream)
{
*
* Checks whether the playback buffer is empty.
*
- * Returns non-zero if empty, or zero if not.
+ * Return: Non-zero if empty, or zero if not.
*/
static inline int snd_pcm_playback_empty(struct snd_pcm_substream *substream)
{
*
* Checks whether the capture buffer is empty.
*
- * Returns non-zero if empty, or zero if not.
+ * Return: Non-zero if empty, or zero if not.
*/
static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream)
{
* snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is CPU-endian, 0 if
+ * Return: 1 if the given PCM format is CPU-endian, 0 if
* opposite, or a negative error code if endian not specified.
*/
int snd_pcm_format_cpu_endian(snd_pcm_format_t format);
* contiguous in kernel virtual space, but not in physical memory. Use this
* if the buffer is accessed by kernel code but not by device DMA.
*
- * Returns 1 if the buffer was changed, 0 if not changed, or a negative error
+ * Return: 1 if the buffer was changed, 0 if not changed, or a negative error
* code.
*/
static int snd_pcm_lib_alloc_vmalloc_buffer
*
* This function works like snd_pcm_lib_alloc_vmalloc_buffer(), but uses
* vmalloc_32(), i.e., the pages are allocated from 32-bit-addressable memory.
+ *
+ * Return: 1 if the buffer was changed, 0 if not changed, or a negative error
+ * code.
*/
static int snd_pcm_lib_alloc_vmalloc_32_buffer
(struct snd_pcm_substream *substream, size_t size);
/**
* snd_pcm_stream_str - Get a string naming the direction of a stream
* @substream: the pcm substream instance
+ *
+ * Return: A string naming the direction of the stream.
*/
static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream)
{
struct snd_soc_dai;
struct snd_ac97_bus_ops;
-/* Digital Audio Interface registration */
-int snd_soc_register_dai(struct device *dev,
- struct snd_soc_dai_driver *dai_drv);
-void snd_soc_unregister_dai(struct device *dev);
-int snd_soc_register_dais(struct device *dev,
- struct snd_soc_dai_driver *dai_drv, size_t count);
-void snd_soc_unregister_dais(struct device *dev, size_t count);
-
/* Digital Audio Interface clocking API.*/
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir);
struct snd_soc_platform_driver;
struct snd_soc_codec;
struct snd_soc_codec_driver;
+struct snd_soc_component;
+struct snd_soc_component_driver;
struct soc_enum;
struct snd_soc_jack;
struct snd_soc_jack_zone;
const struct snd_soc_codec_driver *codec_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai);
void snd_soc_unregister_codec(struct device *dev);
+int snd_soc_register_component(struct device *dev,
+ const struct snd_soc_component_driver *cmpnt_drv,
+ struct snd_soc_dai_driver *dai_drv, int num_dai);
+void snd_soc_unregister_component(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
#endif
};
+struct snd_soc_component_driver {
+ const char *name;
+};
+
+struct snd_soc_component {
+ const char *name;
+ int id;
+ int num_dai;
+ struct device *dev;
+ struct list_head list;
+
+ const struct snd_soc_component_driver *driver;
+};
+
struct snd_soc_dai_link {
/* config - must be set by machine driver */
const char *name; /* Codec name */
--- /dev/null
+#ifndef _SND_SOC_CODEC_TAS5086_H_
+#define _SND_SOC_CODEC_TAS5086_H_
+
+#define TAS5086_CLK_IDX_MCLK 0
+#define TAS5086_CLK_IDX_SCLK 1
+
+#endif /* _SND_SOC_CODEC_TAS5086_H_ */
+++ /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 SNDRV_PCM_FORMAT_G723_24_1B ((__force snd_pcm_format_t) 45) /* 1 sample in 1 byte */
#define SNDRV_PCM_FORMAT_G723_40 ((__force snd_pcm_format_t) 46) /* 8 Samples in 5 bytes */
#define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */
-#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_G723_40_1B
+#define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */
+#define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */
+#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U16_LE
#ifdef SNDRV_LITTLE_ENDIAN
#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE
* Allocates a new struct snd_kcontrol instance and copies the given template
* to the new instance. It does not copy volatile data (access).
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
unsigned int access)
* template. When the access field of ncontrol is 0, it's assumed as
* READWRITE access. When the count field is 0, it's assumes as one.
*
- * Returns the pointer of the newly generated instance, or NULL on failure.
+ * Return: The pointer of the newly generated instance, or %NULL on failure.
*/
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
* snd_ctl_new1() to the given card. Assigns also an unique
* numid used for fast search.
*
- * Returns zero if successful, or a negative error code on failure.
- *
* It frees automatically the control which cannot be added.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ *
*/
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
* and the add_on_replace flag is set, the control is added. If the
* control exists, it is destroyed first.
*
- * Returns zero if successful, or a negative error code on failure.
- *
* It frees automatically the control which cannot be added or replaced.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
bool add_on_replace)
* Removes the control from the card and then releases the instance.
* You don't need to call snd_ctl_free_one(). You must be in
* the write lock - down_write(&card->controls_rwsem).
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
struct snd_ctl_elem_id *id)
* Finds the control instance with the given id, and activate or
* inactivate the control together with notification, if changed.
*
- * Returns 0 if unchanged, 1 if changed, or a negative error code on failure.
+ * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
*/
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int active)
* Finds the control with the old id from the card, and replaces the
* id with the new one.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_ctl_elem_id *dst_id)
*
* Finds the control instance with the given number-id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
* The caller must down card->controls_rwsem before calling this function
* (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
*/
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
*
* Finds the control instance with the given id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
- *
* The caller must down card->controls_rwsem before calling this function
* (if the race condition can happen).
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
*/
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
struct snd_ctl_elem_id *id)
* Sets all required fields in @info to their appropriate values.
* If the control's accessibility is not the default (readable and writable),
* the caller has to fill @info->access.
+ *
+ * Return: Zero.
*/
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
unsigned int items, const char *const names[])
* The data pointer plays a role as the identifier, too, so the
* pointer address must be unique and unchanged.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_device_new(struct snd_card *card, snd_device_type_t type,
void *device_data, struct snd_device_ops *ops)
* callbacks, dev_disconnect and dev_free, corresponding to the state.
* Then release the device.
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
int snd_device_free(struct snd_card *card, void *device_data)
*
* Usually called from snd_card_disconnect().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
int snd_device_disconnect(struct snd_card *card, void *device_data)
* but it can be called later if any new devices are created after
* invocation of snd_card_register().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
int snd_device_register(struct snd_card *card, void *device_data)
* The callbacks (hwdep->ops) must be set on the returned instance
* after this call manually by the caller.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_hwdep_new(struct snd_card *card, char *id, int device,
struct snd_hwdep **rhwdep)
char *nbuf;
nsize = PAGE_ALIGN(nsize);
- nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
+ nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO);
if (! nbuf)
return -ENOMEM;
*
* Outputs the string on the procfs buffer just like printf().
*
- * Returns the size of output string.
+ * Return: The size of output string, or a negative error code.
*/
int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
{
goto __nomem;
data->rbuffer = buffer;
buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
+ buffer->buffer = kzalloc(buffer->len, GFP_KERNEL);
if (buffer->buffer == NULL)
goto __nomem;
}
*
* Reads one line from the buffer and stores the string.
*
- * Returns zero if successful, or 1 if error or EOF.
+ * Return: Zero if successful, or 1 if error or EOF.
*/
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
int c = -1;
+ if (snd_BUG_ON(!buffer || !buffer->buffer))
+ return 1;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
- while (--len > 0) {
+ while (!buffer->stop) {
c = buffer->buffer[buffer->curr++];
- if (c == '\n') {
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- break;
- }
- *line++ = c;
- if (buffer->curr >= buffer->size) {
+ if (buffer->curr >= buffer->size)
buffer->stop = 1;
+ if (c == '\n')
break;
+ if (len) {
+ len--;
+ *line++ = c;
}
}
- while (c != '\n' && !buffer->stop) {
- c = buffer->buffer[buffer->curr++];
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- }
*line = '\0';
return 0;
}
* Parses the original string and copy a token to the given
* string buffer.
*
- * Returns the updated pointer of the original string so that
+ * Return: The updated pointer of the original string so that
* it can be used for the next call.
*/
const char *snd_info_get_str(char *dest, const char *src, int len)
* Usually called from other functions such as
* snd_info_create_card_entry().
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
static struct snd_info_entry *snd_info_create_entry(const char *name)
{
*
* Creates a new info entry and assigns it to the given module.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
const char *name,
*
* Creates a new info entry and assigns it to the given card.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
* For releasing this entry, use snd_device_free() instead of
* snd_info_free_entry().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_card_proc_new(struct snd_card *card, const char *name,
struct snd_info_entry **entryp)
*
* Registers the proc info entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_info_register(struct snd_info_entry * entry)
{
* space for the driver to use freely. The allocated struct is stored
* in the given card_ret pointer.
*
- * Returns zero if successful or a negative error code.
+ * Return: Zero if successful or a negative error code.
*/
int snd_card_create(int idx, const char *xid,
struct module *module, int extra_size,
*
* Disconnects all APIs from the file-operations (user space).
*
- * Returns zero, otherwise a negative error code.
+ * Return: Zero, otherwise a negative error code.
*
* Note: The current implementation replaces all active file->f_op with special
* dummy file operations (they do nothing except release).
* devices automatically. That is, you don't have to release the devices
* by yourself.
*
- * Returns zero. Frees all associated devices and frees the control
+ * Return: Zero. Frees all associated devices and frees the control
* interface associated to given soundcard.
*/
static int snd_card_do_free(struct snd_card *card)
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
- * Returns zero otherwise a negative error code if the registration failed.
+ * Return: Zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
* This function adds the component id string to the supported list.
* The component can be referred from the alsa-lib.
*
- * Returns zero otherwise a negative error code.
+ * Return: Zero otherwise a negative error code.
*/
int snd_component_add(struct snd_card *card, const char *component)
* This linked-list is used to keep tracking the connection state,
* and to avoid the release of busy resources by hotplug.
*
- * Returns zero or a negative error code.
+ * Return: zero or a negative error code.
*/
int snd_card_file_add(struct snd_card *card, struct file *file)
{
* called beforehand, it processes the pending release of
* resources.
*
- * Returns zero or a negative error code.
+ * Return: Zero or a negative error code.
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
*
* Waits until the power-state is changed.
*
+ * Return: Zero if successful, or a negative error code.
+ *
* Note: the power lock must be active before call.
*/
int snd_power_wait(struct snd_card *card, unsigned int power_state)
* @dma: the dma number
* @size: the dma transfer size
*
- * Returns the current pointer in DMA tranfer buffer in bytes
+ * Return: The current pointer in DMA transfer buffer in bytes.
*/
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
*
* Creates a new jack object.
*
- * Returns zero if successful, or a negative error code on failure.
- * On success jjack will be initialised.
+ * Return: Zero if successful, or a negative error code on failure.
+ * On success @jjack will be initialised.
*/
int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jjack)
* using this abstraction.
*
* This function may only be called prior to registration of the jack.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype)
*
* Allocates the physically contiguous pages with the given size.
*
- * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ * Return: The pointer of the buffer, or %NULL if no enough memory.
*/
void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
{
*
* Calls the memory-allocator function for the corresponding
* buffer type.
- *
- * Returns zero if the buffer with the given size is allocated successfully,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
* buffer type. When no space is left, this function reduces the size and
* tries to allocate again. The size actually allocated is stored in
* res_size argument.
- *
- * Returns zero if the buffer with the given size is allocated successfully,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
* Looks for the reserved-buffer list and re-uses if the same buffer
* is found in the list. When the buffer is found, it's removed from the free list.
*
- * Returns the size of buffer if the buffer is found, or zero if not found.
+ * Return: The size of buffer if the buffer is found, or zero if not found.
*/
size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
{
* @id: the buffer id
*
* Reserves the given buffer as a reserved buffer.
- *
- * Returns zero if successful, or a negative code at error.
+ *
+ * Return: Zero if successful, or a negative code on error.
*/
int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
{
*
* Copies the data from mmio-space to user-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
{
*
* Copies the data from user-space to mmio-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
{
FORMAT(G723_24_1B),
FORMAT(G723_40),
FORMAT(G723_40_1B),
+ FORMAT(DSD_U8),
+ FORMAT(DSD_U16_LE),
};
const char *snd_pcm_format_name(snd_pcm_format_t format)
* calling this, i.e. zero must be given to the argument of
* snd_pcm_new().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, struct snd_pcm **rpcm)
* The pcm operators have to be set afterwards to the new instance
* via snd_pcm_set_ops().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
* The interval is changed to the range satisfying both intervals.
* The interval status (min, max, integer, etc.) are evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
unsigned int rats_count, struct snd_ratnum *rats,
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
unsigned int rats_count, struct snd_ratden *rats,
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_list(struct snd_interval *i, unsigned int count,
const unsigned int *list, unsigned int mask)
* @private: the private data pointer passed to function
* @dep: the dependent variables
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
int var,
* @mask: the bitmap mask
*
* Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
* @mask: the 64bit bitmap mask
*
* Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
* @var: hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{
* @max: the maximal value
*
* Apply the min/max range constraint to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
unsigned int min, unsigned int max)
* @l: list
*
* Apply the list of constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned int cond,
* @cond: condition bits
* @var: hw_params variable to apply the ratnums constraint
* @r: struct snd_ratnums constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
* @cond: condition bits
* @var: hw_params variable to apply the ratdens constraint
* @r: struct snd_ratdens constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
* @cond: condition bits
* @width: sample bits width
* @msbits: msbits width
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
unsigned int cond,
* @cond: condition bits
* @var: hw_params variable to apply the step constraint
* @step: step size
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
unsigned int cond,
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the power-of-2 constraint
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
unsigned int cond,
* snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
* @runtime: PCM runtime instance
* @base_rate: the rate at which the hardware does not resample
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
unsigned int base_rate)
* @var: parameter to retrieve
* @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Return the value for field @var if it's fixed in configuration space
- * defined by @params. Return -%EINVAL otherwise.
+ * Return: The value for field @var if it's fixed in configuration space
+ * defined by @params. -%EINVAL otherwise.
*/
int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
*
* Inside configuration space defined by @params remove from @var all
* values > minimum. Reduce configuration space accordingly.
- * Return the minimum.
+ *
+ * Return: The minimum, or a negative error code on failure.
*/
int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values < maximum. Reduce configuration space accordingly.
- * Return the maximum.
+ *
+ * Return: The maximum, or a negative error code on failure.
*/
int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
* The configuration chosen is that obtained fixing in this order:
* first access, first format, first subformat, min channels,
* min rate, min period time, max buffer size, min tick time
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params)
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
* @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.
+ * Return: Zero if successful, or a negative error value.
*/
int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
const struct snd_pcm_chmap_elem *chmap,
*
* Releases the pre-allocated buffer of the given substream.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{
*
* Releases all the pre-allocated buffers on the given pcm.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
{
* destruction time. The dma_buf_id must be unique for all systems
* (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
* Do pre-allocation to all substreams of the given pcm for the
* specified DMA type.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data,
* @substream: the pcm substream instance
* @offset: the buffer offset
*
- * Returns the page struct at the given buffer offset.
* Used as the page callback of PCM ops.
+ *
+ * Return: The page struct at the given buffer offset. %NULL on failure.
*/
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
{
* Allocates the DMA buffer on the BUS type given earlier to
* snd_pcm_lib_preallocate_xxx_pages().
*
- * Returns 1 if the buffer is changed, 0 if not changed, or a negative
+ * Return: 1 if the buffer is changed, 0 if not changed, or a negative
* code on failure.
*/
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
*
* Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
* snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
* @substream: the substream with a buffer allocated by
* snd_pcm_lib_alloc_vmalloc_buffer()
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
{
* @offset: offset in the buffer
*
* This function is to be used as the page callback in the PCM ops.
+ *
+ * Return: The page struct, or %NULL on failure.
*/
struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
unsigned long offset)
.width = 5, .phys = 5, .le = -1, .signd = -1,
.silence = {},
},
+ [SNDRV_PCM_FORMAT_DSD_U8] = {
+ .width = 8, .phys = 8, .le = 1, .signd = 0,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_LE] = {
+ .width = 16, .phys = 16, .le = 1, .signd = 0,
+ .silence = {},
+ },
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
* snd_pcm_format_signed - Check the PCM format is signed linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is signed linear, 0 if unsigned
+ * Return: 1 if the given PCM format is signed linear, 0 if unsigned
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_signed(snd_pcm_format_t format)
* snd_pcm_format_unsigned - Check the PCM format is unsigned linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is unsigned linear, 0 if signed
+ * Return: 1 if the given PCM format is unsigned linear, 0 if signed
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_unsigned(snd_pcm_format_t format)
* snd_pcm_format_linear - Check the PCM format is linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is linear, 0 if not.
+ * Return: 1 if the given PCM format is linear, 0 if not.
*/
int snd_pcm_format_linear(snd_pcm_format_t format)
{
* snd_pcm_format_little_endian - Check the PCM format is little-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is little-endian, 0 if
+ * Return: 1 if the given PCM format is little-endian, 0 if
* big-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_little_endian(snd_pcm_format_t format)
* snd_pcm_format_big_endian - Check the PCM format is big-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is big-endian, 0 if
+ * Return: 1 if the given PCM format is big-endian, 0 if
* little-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_big_endian(snd_pcm_format_t format)
* snd_pcm_format_width - return the bit-width of the format
* @format: the format to check
*
- * Returns the bit-width of the format, or a negative error code
+ * Return: The bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_width(snd_pcm_format_t format)
* snd_pcm_format_physical_width - return the physical bit-width of the format
* @format: the format to check
*
- * Returns the physical bit-width of the format, or a negative error code
+ * Return: The physical bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_physical_width(snd_pcm_format_t format)
* @format: the format to check
* @samples: sampling rate
*
- * Returns the byte size of the given samples for the format, or a
+ * Return: The byte size of the given samples for the format, or a
* negative error code if unknown format.
*/
ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
* @format: the format to check
*
- * Returns the format pattern to fill or NULL if error.
+ * Return: The format pattern to fill or %NULL if error.
*/
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
{
*
* Sets the silence data on the buffer for the given samples.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
{
* Determines the rate_min and rate_max fields from the rates bits of
* the given runtime->hw.
*
- * Returns zero if successful.
+ * Return: Zero if successful.
*/
int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
{
* snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit
* @rate: the sample rate to convert
*
- * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
* SNDRV_PCM_RATE_KNOT for an unknown rate.
*/
unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
* snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate
* @rate_bit: the rate bit to convert
*
- * Returns the sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag
- * or 0 for an unknown rate bit
+ * Return: The sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag
+ * or 0 for an unknown rate bit.
*/
unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit)
{
/**
* snd_pcm_start - start all linked streams
* @substream: the PCM substream instance
+ *
+ * Return: Zero if successful, or a negative error code.
*/
int snd_pcm_start(struct snd_pcm_substream *substream)
{
* @state: PCM state after stopping the stream
*
* The state of each stream is then changed to the given state unconditionally.
+ *
+ * Return: Zero if succesful, or a negative error code.
*/
int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
{
*
* After stopping, the state is changed to SETUP.
* Unlike snd_pcm_stop(), this affects only the given stream.
+ *
+ * Return: Zero if succesful, or a negative error code.
*/
int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
* @substream: the PCM substream
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @substream is %NULL), or a negative error
+ * code.
*/
int snd_pcm_suspend(struct snd_pcm_substream *substream)
{
* @pcm: the PCM instance
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @pcm is %NULL), or a negative error code.
*/
int snd_pcm_suspend_all(struct snd_pcm *pcm)
{
* snd_pcm_prepare - prepare the PCM substream to be triggerable
* @substream: the PCM substream instance
* @file: file to refer f_flags
+ *
+ * Return: Zero if successful, or a negative error code.
*/
static int snd_pcm_prepare(struct snd_pcm_substream *substream,
struct file *file)
*
* Reads the data from the internal buffer.
*
- * Returns the size of read data, or a negative error code on failure.
+ * Return: The size of read data, or a negative error code on failure.
*/
int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
const unsigned char *buffer, int count)
/**
* snd_rawmidi_transmit_empty - check whether the output buffer is empty
* @substream: the rawmidi substream
- *
- * Returns 1 if the internal output buffer is empty, 0 if not.
+ *
+ * Return: 1 if the internal output buffer is empty, 0 if not.
*/
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
* and call snd_rawmidi_transmit_ack() after the transmission is
* finished.
*
- * Returns the size of copied data, or a negative error code on failure.
+ * Return: The size of copied data, or a negative error code on failure.
*/
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
* the given size and updates the condition.
* Call after the transmission is finished.
*
- * Returns the advanced size if successful, or a negative error code on failure.
+ * Return: The advanced size if successful, or a negative error code on failure.
*/
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
*
* Copies data from the buffer to the device and advances the pointer.
*
- * Returns the copied size if successful, or a negative error code on failure.
+ * Return: The copied size if successful, or a negative error code on failure.
*/
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
* Creates a new rawmidi instance.
* Use snd_rawmidi_set_ops() to set the operators to the new instance.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_rawmidi_new(struct snd_card *card, char *id, int device,
int output_count, int input_count,
* This function increments the reference counter of the card instance
* if an associated instance with the given minor number and type is found.
* The caller must call snd_card_unref() appropriately later.
+ *
+ * Return: The user data pointer if the specified device is found. %NULL
+ * otherwise.
*/
void *snd_lookup_minor_data(unsigned int minor, int type)
{
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
* Unregisters the device file already registered via
* snd_register_device().
*
- * Returns zero if sucecessful, or a negative error code on failure
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_unregister_device(int type, struct snd_card *card, int dev)
{
* @name: name string of the control element to create
* @tlv: optional TLV int array for dB information
*
- * Creates a virtual matster control with the given name string.
- * Returns the created control element, or NULL for errors (ENOMEM).
+ * Creates a virtual master control with the given name string.
*
* After creating a vmaster element, you can add the slave controls
* via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
* for dB scale of the master control. It should be a single element
* with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
* #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
+ *
+ * Return: The created control element, or %NULL for errors (ENOMEM).
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
*
* Adds the given hook to the vmaster control element so that it's called
* at each time when the value is changed.
+ *
+ * Return: Zero.
*/
int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
void (*hook)(void *private_data, int),
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART i/o.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)
{
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART output.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)
{
* not the mpu401 instance itself. To access to the mpu401 instance,
* cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).
*
- * Returns zero if successful, or a negative error code.
+ * Return: Zero if successful, or a negative error code.
*/
int snd_mpu401_uart_new(struct snd_card *card, int device,
unsigned short hardware,
*/
- detected_devc = kmalloc(sizeof(sb_devc), GFP_KERNEL);
+ detected_devc = kmemdup(devc, sizeof(sb_devc), GFP_KERNEL);
if (detected_devc == NULL)
{
printk(KERN_ERR "sb: Can't allocate memory for device information\n");
return 0;
}
- memcpy(detected_devc, devc, sizeof(sb_devc));
MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
return 1;
}
goto cleanup_irq;
}
conf_printf(name, hw_config);
- midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
+ midi_devs[devc->my_dev] = kmemdup(&uart401_operations,
+ sizeof(struct midi_operations),
+ GFP_KERNEL);
if (!midi_devs[devc->my_dev]) {
printk(KERN_ERR "uart401: Failed to allocate memory\n");
goto cleanup_unload_mididev;
}
- memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations));
if (owner)
midi_devs[devc->my_dev]->owner = owner;
midi_devs[devc->my_dev]->devc = devc;
- midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
+ midi_devs[devc->my_dev]->converter = kmemdup(&std_midi_synth,
+ sizeof(struct synth_operations),
+ GFP_KERNEL);
+
if (!midi_devs[devc->my_dev]->converter) {
printk(KERN_WARNING "uart401: Failed to allocate memory\n");
goto cleanup_midi_devs;
}
- memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations));
strcpy(midi_devs[devc->my_dev]->info.name, name);
midi_devs[devc->my_dev]->converter->id = "UART401";
midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
* Reads a value from the given register. This will invoke the read
* callback directly after the register check.
*
- * Returns the read value.
+ * Return: The read value.
*/
unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
* Compares the value with the register cache and updates the value
* only when the value is changed.
*
- * Returns 1 if the value is changed, 0 if no change, or a negative
+ * Return: 1 if the value is changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
* Updates the masked-bits on the given register only when the value
* is changed.
*
- * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * Return: 1 if the bits are changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
* snd_ac97_get_short_name - retrieve codec name
* @ac97: the codec instance
*
- * Returns the short identifying name of the codec.
+ * Return: The short identifying name of the codec.
*/
const char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
{
* The AC97 bus instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
void *private_data, struct snd_ac97_bus **rbus)
* The ac97 instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
{
* @powerup: non-zero when power up the part
*
* Update the AC97 powerdown register bits of the given part.
+ *
+ * Return: Zero.
*/
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{
* headphone (true line-out) control as "Master".
* The quirk-list must be terminated with a zero-filled entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
* AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
* status bits.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
{
* It assigns available AC97 slots for given PCMs. If none or only
* some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
* are reduced and might be zero.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
unsigned short pcms_count,
* @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
*
* It locks the specified slots and sets the given rate to AC97 registers.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
enum ac97_pcm_cfg cfg, unsigned short slots)
* @pcm: the ac97 pcm instance
*
* It frees the locked AC97 slots.
+ *
+ * Return: Zero.
*/
int snd_ac97_pcm_close(struct ac97_pcm *pcm)
{
*
* Installs the hardware constraint rules to prevent using double rates and
* more than two channels at the same time.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
{
}
}
+static bool can_be_headset_mic(struct hda_codec *codec,
+ struct auto_pin_cfg_item *item,
+ int seq_number)
+{
+ int attr;
+ unsigned int def_conf;
+ if (item->type != AUTO_PIN_MIC)
+ return false;
+
+ if (item->is_headset_mic || item->is_headphone_mic)
+ return false; /* Already assigned */
+
+ def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (attr <= INPUT_PIN_ATTR_DOCK)
+ return false;
+
+ if (seq_number >= 0) {
+ int seq = get_defcfg_sequence(def_conf);
+ if (seq != seq_number)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
}
}
+ /* Find a pin that could be a headset or headphone mic */
+ if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
+ bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
+ bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
+ if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+
+ /* If we didn't find our sequence number mark, fall back to any sequence number */
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
+ if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
+ continue;
+ if (hsmic) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+ }
+
+ if (hsmic)
+ snd_printdd("Told to look for a headset mic, but didn't find any.\n");
+ if (hpmic)
+ snd_printdd("Told to look for a headphone mic, but didn't find any.\n");
+ }
+
/* FIX-UP:
* If no line-out is defined but multiple HPs are found,
* some of them might be the real line-outs.
*/
static const char *hda_get_input_pin_label(struct hda_codec *codec,
+ const struct auto_pin_cfg_item *item,
hda_nid_t pin, bool check_location)
{
unsigned int def_conf;
switch (get_defcfg_device(def_conf)) {
case AC_JACK_MIC_IN:
+ if (item && item->is_headset_mic)
+ return "Headset Mic";
+ if (item && item->is_headphone_mic)
+ return "Headphone Mic";
if (!check_location)
return "Mic";
attr = snd_hda_get_input_pin_attr(def_conf);
has_multiple_pins = 1;
if (has_multiple_pins && type == AUTO_PIN_MIC)
has_multiple_pins &= check_mic_location_need(codec, cfg, input);
- return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+ return hda_get_input_pin_label(codec, &cfg->inputs[input],
+ cfg->inputs[input].pin,
has_multiple_pins);
}
EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
}
}
if (!name)
- name = hda_get_input_pin_label(codec, nid, true);
+ name = hda_get_input_pin_label(codec, NULL, nid, true);
break;
}
if (!name)
struct auto_pin_cfg_item {
hda_nid_t pin;
int type;
+ unsigned int is_headset_mic:1;
+ unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
};
struct auto_pin_cfg;
};
/* bit-flags for snd_hda_parse_pin_def_config() behavior */
-#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
-#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
+#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_outs : cfg->hp_outs;
+}
+static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_out_pins : cfg->hp_pins;
+}
+static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_outs : cfg->speaker_outs;
+}
+static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_out_pins : cfg->speaker_pins;
+}
+
#endif /* __SOUND_HDA_AUTO_PARSER_H */
struct hda_beep *beep =
container_of(work, struct hda_beep, beep_work);
struct hda_codec *codec = beep->codec;
+ int tone;
if (!beep->enabled)
return;
+ tone = beep->tone;
+ if (tone && !beep->playing) {
+ snd_hda_power_up(codec);
+ beep->playing = 1;
+ }
/* generate tone */
snd_hda_codec_write(codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, beep->tone);
+ AC_VERB_SET_BEEP_CONTROL, tone);
+ if (!tone && beep->playing) {
+ beep->playing = 0;
+ snd_hda_power_down(codec);
+ }
}
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
return 0;
}
+static void turn_off_beep(struct hda_beep *beep)
+{
+ cancel_work_sync(&beep->beep_work);
+ if (beep->playing) {
+ /* turn off beep */
+ snd_hda_codec_write(beep->codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, 0);
+ beep->playing = 0;
+ snd_hda_power_down(beep->codec);
+ }
+}
+
static void snd_hda_do_detach(struct hda_beep *beep)
{
input_unregister_device(beep->dev);
beep->dev = NULL;
- cancel_work_sync(&beep->beep_work);
- /* turn off beep for sure */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
+ turn_off_beep(beep);
}
static int snd_hda_do_attach(struct hda_beep *beep)
enable = !!enable;
if (beep->enabled != enable) {
beep->enabled = enable;
- if (!enable) {
- cancel_work_sync(&beep->beep_work);
- /* turn off beep */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
- }
+ if (!enable)
+ turn_off_beep(beep);
return 1;
}
return 0;
snprintf(beep->phys, sizeof(beep->phys),
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
/* enable linear scale */
- snd_hda_codec_write(codec, nid, 0,
+ snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid;
hda_nid_t nid;
unsigned int enabled:1;
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
+ unsigned int playing:1;
struct work_struct beep_work; /* scheduled task for beep event */
struct mutex mutex;
};
{
struct hda_pincfg *pin;
+ /* the check below may be invalid when pins are added by a fixup
+ * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled
+ * for now
+ */
+ /*
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
return -EINVAL;
+ */
pin = look_up_pincfg(codec, list, nid);
if (!pin) {
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state);
/**
* snd_hda_codec_new - create a HDA codec
#endif
codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
AC_PWRST_EPSS);
- codec->power_filter = default_power_filter;
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
{
if (!hook->hook || !hook->codec)
return;
+ /* don't call vmaster hook in the destructor since it might have
+ * been already destroyed
+ */
+ if (hook->codec->bus->shutdown)
+ return;
switch (hook->mute_mode) {
case HDA_VMUTE_FOLLOW_MASTER:
snd_ctl_sync_vmaster_hook(hook->sw_kctl);
}
/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state)
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
if (power_state == AC_PWRST_D3 &&
get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
}
return power_state;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_eapd_power_filter);
/*
* set power state of the codec, and return the power state
hda_nid_t nid = codec->start_nid;
int i;
- /* don't care if no or standard filter is used */
- if (!codec->power_filter || codec->power_filter == default_power_filter)
+ /* don't care if no filter is used */
+ if (!codec->power_filter)
return;
for (i = 0; i < codec->num_nodes; i++, nid++) {
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
int size = (num + 1) * array->elem_size;
- int oldsize = array->alloced * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = krealloc(array->list, size, GFP_KERNEL);
+ nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
if (!nlist)
return NULL;
- memset(nlist + oldsize, 0, size - oldsize);
array->list = nlist;
array->alloced = num;
}
struct snd_pcm_substream *substream);
int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
+ unsigned int (*get_delay)(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
};
/* PCM information for each substream */
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
+#include "hda_beep.h"
#include "hda_generic.h"
val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
if (val >= 0)
spec->add_stereo_mix_input = !!val;
+ /* the following two are just for compatibility */
val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
if (val >= 0)
- spec->add_out_jack_modes = !!val;
+ spec->add_jack_modes = !!val;
val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
if (val >= 0)
- spec->add_in_jack_modes = !!val;
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
val = snd_hda_get_bool_hint(codec, "power_down_unused");
if (val >= 0)
spec->power_down_unused = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_hp_mic");
+ if (val >= 0)
+ spec->hp_mic = !!val;
+ val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
+ if (val >= 0)
+ spec->suppress_hp_mic_detect = !val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
spec->mixer_nid = val;
/* Primary DAC shared with main surrounds */
BAD_SHARED_SURROUND = 0x100,
/* No independent HP possible */
- BAD_NO_INDEP_HP = 0x40,
+ BAD_NO_INDEP_HP = 0x10,
/* Primary DAC shared with main CLFE */
BAD_SHARED_CLFE = 0x10,
/* Primary DAC shared with extra surrounds */
return badness;
}
-struct badness_table {
- int no_primary_dac; /* no primary DAC */
- int no_dac; /* no secondary DACs */
- int shared_primary; /* primary DAC is shared with main output */
- int shared_surr; /* secondary DAC shared with main or primary */
- int shared_clfe; /* third DAC shared with main or primary */
- int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
-};
-
-static struct badness_table main_out_badness = {
+const struct badness_table hda_main_out_badness = {
.no_primary_dac = BAD_NO_PRIMARY_DAC,
.no_dac = BAD_NO_DAC,
.shared_primary = BAD_NO_PRIMARY_DAC,
.shared_clfe = BAD_SHARED_CLFE,
.shared_surr_main = BAD_SHARED_SURROUND,
};
+EXPORT_SYMBOL_HDA(hda_main_out_badness);
-static struct badness_table extra_out_badness = {
+const struct badness_table hda_extra_out_badness = {
.no_primary_dac = BAD_NO_DAC,
.no_dac = BAD_NO_DAC,
.shared_primary = BAD_NO_EXTRA_DAC,
.shared_clfe = BAD_SHARED_EXTRA_SURROUND,
.shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
};
+EXPORT_SYMBOL_HDA(hda_extra_out_badness);
/* get the DAC of the primary output corresponding to the given array index */
static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
{
struct hda_gen_spec *spec = codec->spec;
struct nid_path *path;
- hda_nid_t dac, pin;
+ hda_nid_t path_dac, dac, pin;
path = snd_hda_get_path_from_idx(codec, path_idx);
if (!path || !path->depth ||
is_nid_contained(path, spec->mixer_nid))
return 0;
- dac = path->path[0];
+ path_dac = path->path[0];
+ dac = spec->private_dac_nids[0];
pin = path->path[path->depth - 1];
path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
if (!path) {
- if (dac != spec->multiout.dac_nids[0])
- dac = spec->multiout.dac_nids[0];
+ if (dac != path_dac)
+ dac = path_dac;
else if (spec->multiout.hp_out_nid[0])
dac = spec->multiout.hp_out_nid[0];
else if (spec->multiout.extra_out_nid[0])
dac = spec->multiout.extra_out_nid[0];
+ else
+ dac = 0;
if (dac)
path = snd_hda_add_new_path(codec, dac, pin,
spec->mixer_nid);
badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
spec->private_dac_nids, spec->out_paths,
- &main_out_badness);
+ spec->main_out_badness);
if (fill_mio_first &&
cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
spec->multiout.hp_out_nid,
spec->hp_paths,
- &extra_out_badness);
+ spec->extra_out_badness);
if (err < 0)
return err;
badness += err;
cfg->speaker_pins,
spec->multiout.extra_out_nid,
spec->speaker_paths,
- &extra_out_badness);
+ spec->extra_out_badness);
if (err < 0)
return err;
badness += err;
* independent HP controls
*/
+/* update HP auto-mute state too */
+static void update_hp_automute_hook(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->hp_automute_hook)
+ spec->hp_automute_hook(codec, NULL);
+ else
+ snd_hda_gen_hp_automute(codec, NULL);
+}
+
static int indep_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
else
*dacp = spec->alt_dac_nid;
- /* update HP auto-mute state too */
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
-
+ update_hp_automute_hook(codec);
ret = 1;
}
unlock:
static void update_automute_all(struct hda_codec *codec);
+/* Default value to be passed as aamix argument for snd_hda_activate_path();
+ * used for output paths
+ */
+static bool aamix_default(struct hda_gen_spec *spec)
+{
+ return !spec->have_aamix_ctl || spec->aamix_mode;
+}
+
static int set_multi_io(struct hda_codec *codec, int idx, bool output)
{
struct hda_gen_spec *spec = codec->spec;
if (output) {
set_pin_target(codec, nid, PIN_OUT, true);
- snd_hda_activate_path(codec, path, true, true);
+ snd_hda_activate_path(codec, path, true, aamix_default(spec));
set_pin_eapd(codec, nid, true);
} else {
set_pin_eapd(codec, nid, false);
- snd_hda_activate_path(codec, path, false, true);
+ snd_hda_activate_path(codec, path, false, aamix_default(spec));
set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
path_power_down_sync(codec, path);
}
snd_hda_activate_path(codec, mix_path, true, true);
path_power_down_sync(codec, nomix_path);
} else {
- snd_hda_activate_path(codec, mix_path, false, true);
- snd_hda_activate_path(codec, nomix_path, true, true);
+ snd_hda_activate_path(codec, mix_path, false, false);
+ snd_hda_activate_path(codec, nomix_path, true, false);
path_power_down_sync(codec, mix_path);
}
}
static void call_update_outputs(struct hda_codec *codec);
/* for shared I/O, change the pin-control accordingly */
-static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force)
{
struct hda_gen_spec *spec = codec->spec;
+ bool as_mic;
unsigned int val;
- hda_nid_t pin = spec->autocfg.inputs[1].pin;
- /* NOTE: this assumes that there are only two inputs, the
- * first is the real internal mic and the second is HP/mic jack.
- */
+ hda_nid_t pin;
- val = snd_hda_get_default_vref(codec, pin);
+ pin = spec->hp_mic_pin;
+ as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx;
+
+ if (!force) {
+ val = snd_hda_codec_get_pin_target(codec, pin);
+ if (as_mic) {
+ if (val & PIN_IN)
+ return;
+ } else {
+ if (val & PIN_OUT)
+ return;
+ }
+ }
- /* This pin does not have vref caps - let's enable vref on pin 0x18
- instead, as suggested by Realtek */
+ val = snd_hda_get_default_vref(codec, pin);
+ /* if the HP pin doesn't support VREF and the codec driver gives an
+ * alternative pin, set up the VREF on that pin instead
+ */
if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
if (vref_val != AC_PINCTL_VREF_HIZ)
snd_hda_set_pin_ctl_cache(codec, vref_pin,
- PIN_IN | (set_as_mic ? vref_val : 0));
+ PIN_IN | (as_mic ? vref_val : 0));
}
- val = set_as_mic ? val | PIN_IN : PIN_HP;
- set_pin_target(codec, pin, val, true);
-
- spec->automute_speaker = !set_as_mic;
- call_update_outputs(codec);
+ if (!spec->hp_mic_jack_modes) {
+ if (as_mic)
+ val |= PIN_IN;
+ else
+ val = PIN_HP;
+ set_pin_target(codec, pin, val, true);
+ update_hp_automute_hook(codec);
+ }
}
/* create a shared input with the headphone out */
-static int create_shared_input(struct hda_codec *codec)
+static int create_hp_mic(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int defcfg;
hda_nid_t nid;
- /* only one internal input pin? */
- if (cfg->num_inputs != 1)
- return 0;
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
- if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ if (!spec->hp_mic) {
+ if (spec->suppress_hp_mic_detect)
+ return 0;
+ /* automatic detection: only if no input or a single internal
+ * input pin is found, try to detect the shared hp/mic
+ */
+ if (cfg->num_inputs > 1)
+ return 0;
+ else if (cfg->num_inputs == 1) {
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+ }
+ }
+
+ spec->hp_mic = 0; /* clear once */
+ if (cfg->num_inputs >= AUTO_CFG_MAX_INS)
return 0;
- if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
- else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
- nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
- else
- return 0; /* both not available */
+ nid = 0;
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0)
+ nid = cfg->line_out_pins[0];
+ else if (cfg->hp_outs > 0)
+ nid = cfg->hp_pins[0];
+ if (!nid)
+ return 0;
if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
return 0; /* no input */
- cfg->inputs[1].pin = nid;
- cfg->inputs[1].type = AUTO_PIN_MIC;
- cfg->num_inputs = 2;
- spec->shared_mic_hp = 1;
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC;
+ cfg->inputs[cfg->num_inputs].is_headphone_mic = 1;
+ cfg->num_inputs++;
+ spec->hp_mic = 1;
+ spec->hp_mic_pin = nid;
+ /* we can't handle auto-mic together with HP-mic */
+ spec->suppress_auto_mic = 1;
snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid);
return 0;
}
/*
* output jack mode
*/
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
+
+static const char * const out_jack_texts[] = {
+ "Line Out", "Headphone Out",
+};
+
static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char * const texts[] = {
- "Line Out", "Headphone Out",
- };
- return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
}
static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
;
}
+static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->add_jack_modes) {
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+ if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
+ return 2;
+ }
+ return 1;
+}
+
static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
hda_nid_t *pins)
{
for (i = 0; i < num_pins; i++) {
hda_nid_t pin = pins[i];
- unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
- if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) {
+ if (pin == spec->hp_mic_pin) {
+ int ret = create_hp_mic_jack_mode(codec, pin);
+ if (ret < 0)
+ return ret;
+ continue;
+ }
+ if (get_out_jack_num_items(codec, pin) > 1) {
struct snd_kcontrol_new *knew;
char name[44];
get_jack_mode_name(codec, pin, name, sizeof(name));
.put = in_jack_mode_put,
};
+static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int nitems = 0;
+ if (spec->add_jack_modes)
+ nitems = hweight32(get_vref_caps(codec, pin));
+ return nitems ? nitems : 1;
+}
+
static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
- unsigned int defcfg;
struct snd_kcontrol_new *knew;
char name[44];
+ unsigned int defcfg;
+
+ if (pin == spec->hp_mic_pin)
+ return 0; /* already done in create_out_jack_mode() */
/* no jack mode for fixed pins */
defcfg = snd_hda_codec_get_pincfg(codec, pin);
return 0;
/* no multiple vref caps? */
- if (hweight32(get_vref_caps(codec, pin)) <= 1)
+ if (get_in_jack_num_items(codec, pin) <= 1)
return 0;
get_jack_mode_name(codec, pin, name, sizeof(name));
return 0;
}
+/*
+ * HP/mic shared jack mode
+ */
+static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ const char *text = NULL;
+ int idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = out_jacks + in_jacks;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ idx = uinfo->value.enumerated.item;
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ text = out_jack_texts[idx];
+ else
+ text = "Headphone Out";
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ text = vref_texts[get_vref_idx(vref_caps, idx)];
+ } else
+ text = "Mic In";
+ }
+
+ strcpy(uinfo->value.enumerated.name, text);
+ return 0;
+}
+
+static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
+{
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
+ int idx = 0;
+
+ if (val & PIN_OUT) {
+ if (out_jacks > 1 && val == PIN_HP)
+ idx = 1;
+ } else if (val & PIN_IN) {
+ idx = out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val &= AC_PINCTL_VREFEN;
+ idx += cvt_from_vref_idx(vref_caps, val);
+ }
+ }
+ return idx;
+}
+
+static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ ucontrol->value.enumerated.item[0] =
+ get_cur_hp_mic_jack_mode(codec, nid);
+ return 0;
+}
+
+static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val, oldval, idx;
+
+ oldval = get_cur_hp_mic_jack_mode(codec, nid);
+ idx = ucontrol->value.enumerated.item[0];
+ if (oldval == idx)
+ return 0;
+
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ val = idx ? PIN_HP : PIN_OUT;
+ else
+ val = PIN_HP;
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ val &= ~(AC_PINCTL_VREFEN | PIN_HP);
+ val |= get_vref_idx(vref_caps, idx) | PIN_IN;
+ } else
+ val = snd_hda_get_default_vref(codec, nid);
+ }
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ update_hp_automute_hook(codec);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = hp_mic_jack_mode_info,
+ .get = hp_mic_jack_mode_get,
+ .put = hp_mic_jack_mode_put,
+};
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (get_out_jack_num_items(codec, pin) <= 1 &&
+ get_in_jack_num_items(codec, pin) <= 1)
+ return 0; /* no need */
+ knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
+ &hp_mic_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ spec->hp_mic_jack_modes = 1;
+ return 0;
+}
/*
* Parse input paths
unsigned int ok_bits;
int i, n, nums;
- again:
nums = 0;
ok_bits = 0;
for (n = 0; n < spec->num_adc_nids; n++) {
}
if (!ok_bits) {
- if (spec->shared_mic_hp) {
- spec->shared_mic_hp = 0;
- imux->num_items = 1;
- goto again;
- }
-
/* check whether ADC-switch is possible */
for (i = 0; i < imux->num_items; i++) {
for (n = 0; n < spec->num_adc_nids; n++) {
spec->num_adc_nids = nums;
}
- if (imux->num_items == 1 || spec->shared_mic_hp) {
+ if (imux->num_items == 1 ||
+ (imux->num_items == 2 && spec->hp_mic)) {
snd_printdd("hda-codec: reducing to a single ADC\n");
spec->num_adc_nids = 1; /* reduce to a single ADC */
}
snd_hda_get_path_idx(codec, path);
if (!imux_added) {
+ if (spec->hp_mic_pin == pin)
+ spec->hp_mic_mux_idx = imux->num_items;
spec->imux_pins[imux->num_items] = pin;
snd_hda_add_imux_item(imux, label, cfg_idx, NULL);
imux_added = true;
val = PIN_IN;
if (cfg->inputs[i].type == AUTO_PIN_MIC)
val |= snd_hda_get_default_vref(codec, pin);
- set_pin_target(codec, pin, val, false);
+ if (pin != spec->hp_mic_pin)
+ set_pin_target(codec, pin, val, false);
if (mixer) {
if (is_reachable_path(codec, pin, mixer)) {
if (err < 0)
return err;
- if (spec->add_in_jack_modes) {
+ if (spec->add_jack_modes) {
err = create_in_jack_mode(codec, pin);
if (err < 0)
return err;
spec->cur_mux[adc_idx] = idx;
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
+ if (spec->hp_mic)
+ update_hp_mic(codec, adc_idx, false);
if (spec->dyn_adc_switch)
dyn_adc_pcm_resetup(codec, idx);
for (i = 0; i < num_pins; i++) {
hda_nid_t nid = pins[i];
- unsigned int val;
+ unsigned int val, oldval;
if (!nid)
break;
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
/* don't reset VREF value in case it's controlling
* the amp (see alc861_fixup_asus_amp_vref_0f())
*/
if (spec->keep_vref_in_automute)
- val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
+ val = oldval & ~PIN_HP;
else
val = 0;
if (!mute)
- val |= snd_hda_codec_get_pin_target(codec, nid);
+ val |= oldval;
/* here we call update_pin_ctl() so that the pinctl is changed
* without changing the pinctl target value;
* the original target value will be still referred at the
* in general, HP pins/amps control should be enabled in all cases,
* but currently set only for master_mute, just to be safe
*/
- if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
- do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
spec->autocfg.hp_pins, spec->master_mute);
if (!spec->automute_speaker)
{
struct hda_gen_spec *spec = codec->spec;
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
+ update_hp_automute_hook(codec);
if (spec->line_automute_hook)
spec->line_automute_hook(codec, NULL);
else
cfg = &spec->autocfg;
}
+ if (!spec->main_out_badness)
+ spec->main_out_badness = &hda_main_out_badness;
+ if (!spec->extra_out_badness)
+ spec->extra_out_badness = &hda_extra_out_badness;
+
fill_all_dac_nids(codec);
if (!cfg->line_outs) {
err = create_loopback_mixing_ctl(codec);
if (err < 0)
return err;
- err = create_shared_input(codec);
+ err = create_hp_mic(codec);
if (err < 0)
return err;
err = create_input_ctls(codec);
if (err < 0)
return err;
- if (!spec->shared_mic_hp) {
- err = check_auto_mic_availability(codec);
- if (err < 0)
- return err;
- }
+ err = check_auto_mic_availability(codec);
+ if (err < 0)
+ return err;
err = create_capture_mixers(codec);
if (err < 0)
if (err < 0)
return err;
- if (spec->add_out_jack_modes) {
+ if (spec->add_jack_modes) {
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = create_out_jack_modes(codec, cfg->line_outs,
cfg->line_out_pins);
if (spec->power_down_unused)
codec->power_filter = snd_hda_gen_path_power_filter;
+ if (!spec->no_analog && spec->beep_nid) {
+ err = snd_hda_attach_beep_device(codec, spec->beep_nid);
+ if (err < 0)
+ return err;
+ }
+
return 1;
}
EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config);
free_kctls(spec); /* no longer needed */
- if (spec->shared_mic_hp) {
- int err;
- int nid = spec->autocfg.inputs[1].pin;
- err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
- if (err < 0)
- return err;
- err = snd_hda_jack_detect_enable(codec, nid, 0);
- if (err < 0)
- return err;
- }
-
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
if (err < 0)
return err;
return;
pin = path->path[path->depth - 1];
restore_pin_ctl(codec, pin);
- snd_hda_activate_path(codec, path, path->active, true);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(codec->spec));
set_pin_eapd(codec, pin, path->active);
}
if (!spec->multi_io[i].ctl_in)
spec->multi_io[i].ctl_in =
snd_hda_codec_get_pin_target(codec, pin);
- snd_hda_activate_path(codec, path, path->active, true);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(spec));
}
}
snd_hda_activate_path(codec, path, active, false);
}
}
+ if (spec->hp_mic)
+ update_hp_mic(codec, c, true);
}
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[0]);
-
if (spec->cap_sync_hook)
spec->cap_sync_hook(codec, NULL);
}
*/
void snd_hda_gen_free(struct hda_codec *codec)
{
+ snd_hda_detach_beep_device(codec);
snd_hda_gen_spec_free(codec->spec);
kfree(codec->spec);
codec->spec = NULL;
HDA_GEN_PCM_ACT_CLOSE,
};
+/* DAC assignment badness table */
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+extern const struct badness_table hda_main_out_badness;
+extern const struct badness_table hda_extra_out_badness;
+
struct hda_gen_spec {
char stream_name_analog[32]; /* analog PCM stream */
const struct hda_pcm_stream *stream_analog_playback;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ /* shared hp/mic */
hda_nid_t shared_mic_vref_pin;
+ hda_nid_t hp_mic_pin;
+ int hp_mic_mux_idx;
/* DAC/ADC lists */
int num_all_dacs;
/* other parse behavior flags */
unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
- unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+ unsigned int hp_mic:1; /* Allow HP as a mic-in */
+ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
unsigned int indep_hp:1; /* independent HP supported */
unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
- unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
- unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
+ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
unsigned int power_down_unused:1; /* power down unused widgets */
/* other internal flags */
unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
unsigned int indep_hp_enabled:1; /* independent HP enabled */
unsigned int have_aamix_ctl:1;
+ unsigned int hp_mic_jack_modes:1;
+
+ /* badness tables for output path evaluations */
+ const struct badness_table *main_out_badness;
+ const struct badness_table *extra_out_badness;
/* loopback mixing mode */
bool aamix_mode;
+ /* digital beep */
+ hda_nid_t beep_nid;
+
/* for virtual master */
hda_nid_t vmaster_nid;
unsigned int vmaster_tlv[4];
tc->cycle_last = last;
}
+static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ u64 codec_frames, codec_nsecs;
+
+ if (!hinfo->ops.get_delay)
+ return nsec;
+
+ codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
struct timespec *ts)
{
nsec = timecounter_read(&azx_dev->azx_tc);
nsec = div_u64(nsec, 3); /* can be optimized */
+ nsec = azx_adjust_codec_delay(substream, nsec);
*ts = ns_to_timespec(nsec);
struct azx_dev *azx_dev,
bool with_check)
{
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
unsigned int pos;
- int stream = azx_dev->substream->stream;
+ int stream = substream->stream;
+ struct hda_pcm_stream *hinfo = apcm->hinfo[stream];
int delay = 0;
switch (chip->position_fix[stream]) {
pos = 0;
/* calculate runtime delay from LPIB */
- if (azx_dev->substream->runtime &&
+ if (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);
delay = 0;
chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
}
- azx_dev->substream->runtime->delay =
- bytes_to_frames(azx_dev->substream->runtime, delay);
+ delay = bytes_to_frames(substream->runtime, delay);
}
+
+ if (substream->runtime) {
+ if (hinfo->ops.get_delay)
+ delay += hinfo->ops.get_delay(hinfo, apcm->codec,
+ substream);
+ substream->runtime->delay = delay;
+ }
+
trace_azx_get_position(chip, azx_dev, pos, delay);
return pos;
}
}
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
- const struct auto_pin_cfg *cfg)
+ const struct auto_pin_cfg *cfg,
+ const char *base_name)
{
unsigned int def_conf, conn;
char name[44];
phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
!is_jack_detectable(codec, nid);
- snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
+ if (base_name) {
+ strlcpy(name, base_name, sizeof(name));
+ idx = 0;
+ } else
+ snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
if (phantom_jack)
/* Example final name: "Internal Mic Phantom Jack" */
strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
const hda_nid_t *p;
int i, err;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ /* If we have headphone mics; make sure they get the right name
+ before grabbed by output pins */
+ if (cfg->inputs[i].is_headphone_mic) {
+ if (auto_cfg_hp_outs(cfg) == 1)
+ err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
+ cfg, "Headphone Mic");
+ else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin,
+ cfg, "Headphone Mic");
+ } else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
- err = add_jack_kctl(codec, *p, cfg);
+ err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
- err = add_jack_kctl(codec, *p, cfg);
+ err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
if (*p == *cfg->line_out_pins) /* might be duplicated */
break;
- err = add_jack_kctl(codec, *p, cfg);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg);
+ err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
- err = add_jack_kctl(codec, *p, cfg);
+ err = add_jack_kctl(codec, *p, cfg, NULL);
if (err < 0)
return err;
}
- err = add_jack_kctl(codec, cfg->dig_in_pin, cfg);
+ err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
if (err < 0)
return err;
- err = add_jack_kctl(codec, cfg->mono_out_pin, cfg);
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
if (err < 0)
return err;
return 0;
return (state != target_state);
}
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+
/*
* AMP control callbacks
*/
hda_nid_t eapd_nid;
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- hda_nid_t beep_dev_nid;
#ifdef ENABLE_AD_STATIC_QUIRKS
const struct snd_kcontrol_new *mixers[6];
.build_controls = ad198x_auto_build_controls,
.build_pcms = snd_hda_gen_build_pcms,
.init = snd_hda_gen_init,
- .free = ad198x_free,
+ .free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status,
if (err < 0)
return err;
- if (spec->beep_dev_nid) {
- err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid);
- if (err < 0)
- return err;
- }
-
codec->patch_ops = ad198x_auto_patch_ops;
return 0;
codec->inv_eapd = 1;
spec->gen.mixer_nid = 0x07;
- spec->beep_dev_nid = 0x19;
+ spec->gen.beep_nid = 0x19;
set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
/* AD1986A has a hardware problem that it can't share a stream
err = ad198x_parse_auto_config(codec);
if (err < 0) {
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
return err;
spec = codec->spec;
- spec->beep_dev_nid = 0x10;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
err = ad198x_parse_auto_config(codec);
if (err < 0)
return 0;
error:
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
spec = codec->spec;
spec->gen.mixer_nid = 0x0e;
- spec->beep_dev_nid = 0x10;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
return 0;
error:
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
spec->gen.mixer_nid = 0x20;
spec->gen.mixer_merge_nid = 0x21;
- spec->beep_dev_nid = 0x10;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
err = ad198x_parse_auto_config(codec);
if (err < 0)
return 0;
error:
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
spec = codec->spec;
spec->gen.mixer_nid = 0x20;
- spec->beep_dev_nid = 0x10;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
return 0;
error:
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
spec->gen.mixer_nid = 0x20;
spec->gen.mixer_merge_nid = 0x21;
- spec->beep_dev_nid = 0x10;
+ spec->gen.beep_nid = 0x10;
set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
err = ad198x_parse_auto_config(codec);
if (err < 0)
return 0;
error:
- ad198x_free(codec);
+ snd_hda_gen_free(codec);
return err;
}
/* Effects values size*/
#define EFFECT_VALS_MAX_COUNT 12
+/* Latency introduced by DSP blocks in milliseconds. */
+#define DSP_CAPTURE_INIT_LATENCY 0
+#define DSP_CRYSTAL_VOICE_LATENCY 124
+#define DSP_PLAYBACK_INIT_LATENCY 13
+#define DSP_PLAY_ENHANCEMENT_LATENCY 30
+#define DSP_SPEAKER_OUT_LATENCY 7
+
struct ct_effect {
char name[44];
hda_nid_t nid;
long voicefx_val;
long cur_mic_boost;
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+
#ifdef ENABLE_TUNING_CONTROLS
long cur_ctl_vals[TUNING_CTLS_COUNT];
#endif
return 0;
}
+static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_PLAYBACK_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* Add latency if playback enhancement and either effect is enabled. */
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) {
+ if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) ||
+ (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID]))
+ latency += DSP_PLAY_ENHANCEMENT_LATENCY;
+ }
+
+ /* Applying Speaker EQ adds latency as well. */
+ if (spec->cur_out_type == SPEAKER_OUT)
+ latency += DSP_SPEAKER_OUT_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
/*
* Digital out
*/
return 0;
}
+static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_CAPTURE_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ latency += DSP_CRYSTAL_VOICE_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
/*
* Controls stuffs.
*/
return err < 0 ? err : 0;
}
+static void ca0132_unsol_hp_delayed(struct work_struct *work)
+{
+ struct ca0132_spec *spec = container_of(
+ to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+ ca0132_select_out(spec->codec);
+ snd_hda_jack_report_sync(spec->codec);
+}
+
static void ca0132_set_dmic(struct hda_codec *codec, int enable);
static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
.channels_max = 6,
.ops = {
.prepare = ca0132_playback_pcm_prepare,
- .cleanup = ca0132_playback_pcm_cleanup
+ .cleanup = ca0132_playback_pcm_cleanup,
+ .get_delay = ca0132_playback_pcm_delay,
},
};
.channels_max = 2,
.ops = {
.prepare = ca0132_capture_pcm_prepare,
- .cleanup = ca0132_capture_pcm_cleanup
+ .cleanup = ca0132_capture_pcm_cleanup,
+ .get_delay = ca0132_capture_pcm_delay,
},
};
static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
{
- snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res);
-
+ struct ca0132_spec *spec = codec->spec;
if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
ca0132_process_dsp_response(codec);
switch (res) {
case UNSOL_TAG_HP:
- ca0132_select_out(codec);
- snd_hda_jack_report_sync(codec);
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ queue_delayed_work(codec->bus->workq,
+ &spec->unsol_hp_work,
+ msecs_to_jiffies(500));
break;
case UNSOL_TAG_AMIC1:
ca0132_select_mic(codec);
{
struct ca0132_spec *spec = codec->spec;
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
snd_hda_power_up(codec);
snd_hda_sequence_write(codec, spec->base_exit_verbs);
ca0132_exit_chip(codec);
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ spec->codec = codec;
spec->num_mixers = 1;
spec->mixers[0] = ca0132_mixer;
spec->init_verbs[1] = ca0132_init_verbs1;
spec->num_init_verbs = 2;
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
+
ca0132_init_chip(codec);
ca0132_config(codec);
enum {
CS421X_CDB4210,
CS421X_SENSE_B,
+ CS421X_STUMPY,
};
/* Vendor-specific processing widget */
/* CS4210 board names */
static const struct hda_model_fixup cs421x_models[] = {
{ .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
{}
};
{} /* terminator */
};
+/* Stumpy ChromeBox */
+static const struct hda_pintbl stumpy_pincfgs[] = {
+ { 0x05, 0x022120f0 },
+ { 0x06, 0x901700f0 },
+ { 0x07, 0x02a120f0 },
+ { 0x08, 0x77a70037 },
+ { 0x09, 0x77a6003e },
+ { 0x0a, 0x434510f0 },
+ {} /* terminator */
+};
+
/* 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)
[CS421X_SENSE_B] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs421x_fixup_sense_b,
- }
+ },
+ [CS421X_STUMPY] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stumpy_pincfgs,
+ },
};
static const struct hda_verb cs421x_coef_init_verbs[] = {
#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
+static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ spec->gen.beep_nid = nid;
+ spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+}
/* additional beep mixers; the actual parameters are overwritten at build */
static const struct snd_kcontrol_new cxt_beep_mixer[] = {
HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
return 0;
}
-static void cx_auto_free(struct hda_codec *codec)
-{
- snd_hda_detach_beep_device(codec);
- snd_hda_gen_free(codec);
-}
-
static const struct hda_codec_ops cx_auto_patch_ops = {
.build_controls = cx_auto_build_controls,
.build_pcms = snd_hda_gen_build_pcms,
.init = snd_hda_gen_init,
- .free = cx_auto_free,
+ .free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status,
switch (codec->vendor_id) {
case 0x14f15045:
codec->single_adc_amp = 1;
- codec->power_filter = NULL; /* Needs speaker amp to D3 to avoid click */
break;
case 0x14f15047:
codec->pin_amp_workaround = 1;
goto error;
codec->patch_ops = cx_auto_patch_ops;
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, get_amp_nid_(spec->beep_amp));
/* Some laptops with Conexant chips show stalls in S3 resume,
* which falls into the single-cmd mode.
module_param(static_hdmi_pcm, bool, 0644);
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support N independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 8
-#define MAX_HDMI_PINS 8
-
struct hdmi_spec_per_cvt {
hda_nid_t cvt_nid;
int assigned;
bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */
+ char pcm_name[8]; /* filled in build_pcm callbacks */
};
struct hdmi_spec {
int num_cvts;
- struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
- hda_nid_t cvt_nids[MAX_HDMI_CVTS];
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
int num_pins;
- struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
- struct hda_pcm pcm_rec[MAX_HDMI_PINS];
+ struct snd_array pins; /* struct hdmi_spec_per_pin */
+ struct snd_array pcm_rec; /* struct hda_pcm */
unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld;
* HDMI routines
*/
+#define get_pin(spec, idx) \
+ ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
+#define get_cvt(spec, idx) \
+ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
+#define get_pcm_rec(spec, idx) \
+ ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
+
static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)
{
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (spec->pins[pin_idx].pin_nid == pin_nid)
+ if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
return pin_idx;
snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+ if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
return pin_idx;
snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
int cvt_idx;
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
- if (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
return cvt_idx;
snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
pin_idx = kcontrol->private_value;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &get_pin(spec, pin_idx)->sink_eld;
mutex_lock(&eld->lock);
uinfo->count = eld->eld_valid ? eld->eld_size : 0;
int pin_idx;
pin_idx = kcontrol->private_value;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &get_pin(spec, pin_idx)->sink_eld;
mutex_lock(&eld->lock);
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
kctl->private_value = pin_idx;
kctl->id.device = device;
- err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
+ err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
if (err < 0)
return err;
- spec->pins[pin_idx].eld_ctl = kctl;
+ get_pin(spec, pin_idx)->eld_ctl = kctl;
return 0;
}
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
int channels = substream->runtime->channels;
struct hdmi_eld *eld;
int ca;
union audio_infoframe ai;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &per_pin->sink_eld;
if (!eld->monitor_present)
return;
if (pin_idx < 0)
return;
- hdmi_present_sense(&spec->pins[pin_idx], 1);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
snd_hda_jack_report_sync(codec);
}
hdmi_non_intrinsic_event(codec, res);
}
+static void haswell_verify_pin_D0(struct hda_codec *codec, hda_nid_t nid)
+{
+ int pwr, lamp, ramp;
+
+ pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
+ if (pwr != AC_PWRST_D0) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(40);
+ pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
+ snd_printd("Haswell HDMI audio: Power for pin 0x%x is now D%d\n", nid, pwr);
+ }
+
+ lamp = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT | AC_AMP_GET_OUTPUT);
+ ramp = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_RIGHT | AC_AMP_GET_OUTPUT);
+ if (lamp != ramp) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT | lamp);
+
+ lamp = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT | AC_AMP_GET_OUTPUT);
+ ramp = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_RIGHT | AC_AMP_GET_OUTPUT);
+ snd_printd("Haswell HDMI audio: Mute after set on pin 0x%x: [0x%x 0x%x]\n", nid, lamp, ramp);
+ }
+}
+
/*
* Callbacks
*/
int pinctl;
int new_pinctl = 0;
+ if (codec->vendor_id == 0x80862807)
+ haswell_verify_pin_D0(codec, pin_nid);
+
if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
- per_pin = &spec->pins[pin_idx];
+ per_pin = get_pin(spec, pin_idx);
eld = &per_pin->sink_eld;
/* Dynamically assign converter to stream */
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = get_cvt(spec, cvt_idx);
/* Must not already be assigned */
if (per_cvt->assigned)
static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
return 0;
- if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
- return -E2BIG;
-
if (codec->vendor_id == 0x80862807)
intel_haswell_fixup_connect_list(codec, pin_nid);
pin_idx = spec->num_pins;
- per_pin = &spec->pins[pin_idx];
+ per_pin = snd_array_new(&spec->pins);
+ if (!per_pin)
+ return -ENOMEM;
per_pin->pin_nid = pin_nid;
per_pin->non_pcm = false;
static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
- int cvt_idx;
struct hdmi_spec_per_cvt *per_cvt;
unsigned int chans;
int err;
- if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
- return -E2BIG;
-
chans = get_wcaps(codec, cvt_nid);
chans = get_wcaps_channels(chans);
- cvt_idx = spec->num_cvts;
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_cvt)
+ return -ENOMEM;
per_cvt->cvt_nid = cvt_nid;
per_cvt->channels_min = 2;
if (err < 0)
return err;
- spec->cvt_nids[spec->num_cvts++] = cvt_nid;
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
+ spec->num_cvts++;
return 0;
}
/*
*/
-static char *get_hdmi_pcm_name(int idx)
-{
- static char names[MAX_HDMI_PINS][8];
- sprintf(&names[idx][0], "HDMI %d", idx);
- 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;
hda_nid_t cvt_nid = hinfo->nid;
struct hdmi_spec *spec = codec->spec;
int pin_idx = hinfo_to_pin_index(spec, hinfo);
- hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+ hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid;
bool non_pcm;
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
if (snd_BUG_ON(cvt_idx < 0))
return -EINVAL;
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = get_cvt(spec, cvt_idx);
snd_BUG_ON(!per_cvt->assigned);
per_cvt->assigned = 0;
pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
- per_pin = &spec->pins[pin_idx];
+ per_pin = get_pin(spec, pin_idx);
snd_hda_spdif_ctls_unassign(codec, pin_idx);
per_pin->chmap_set = false;
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];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
int i;
for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
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];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
unsigned int ctl_idx;
struct snd_pcm_substream *substream;
unsigned char chmap[8];
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
-
- info = &spec->pcm_rec[pin_idx];
- info->name = get_hdmi_pcm_name(pin_idx);
+ struct hdmi_spec_per_pin *per_pin;
+
+ per_pin = get_pin(spec, pin_idx);
+ sprintf(per_pin->pcm_name, "HDMI %d", pin_idx);
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = per_pin->pcm_name;
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->own_chmap = true;
}
codec->num_pcms = spec->num_pins;
- codec->pcm_info = spec->pcm_rec;
+ codec->pcm_info = spec->pcm_rec.list;
return 0;
}
{
char hdmi_str[32] = "HDMI/DP";
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
- int pcmdev = spec->pcm_rec[pin_idx].device;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ int pcmdev = get_pcm_rec(spec, pin_idx)->device;
if (pcmdev > 0)
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
err = generic_hdmi_build_jack(codec, pin_idx);
if (err < 0)
snd_hda_spdif_ctls_unassign(codec, pin_idx);
/* add control for ELD Bytes */
- err = hdmi_create_eld_ctl(codec,
- pin_idx,
- spec->pcm_rec[pin_idx].device);
+ err = hdmi_create_eld_ctl(codec, pin_idx,
+ get_pcm_rec(spec, pin_idx)->device);
if (err < 0)
return err;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *eld = &per_pin->sink_eld;
per_pin->codec = codec;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
hdmi_init_pin(codec, pin_nid);
return 0;
}
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
+ snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
+}
+
+static void hdmi_array_free(struct hdmi_spec *spec)
+{
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+ snd_array_free(&spec->pcm_rec);
+}
+
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *eld = &per_pin->sink_eld;
cancel_delayed_work(&per_pin->work);
}
flush_workqueue(codec->bus->workq);
+ hdmi_array_free(spec);
kfree(spec);
}
/* override pins connection list */
snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
+ nconns = max(spec->num_cvts, 4);
snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
}
return -ENOMEM;
codec->spec = spec;
+ hdmi_array_init(spec, 4);
snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
static int simple_playback_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
+ struct hda_pcm *info;
unsigned int chans;
struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- chans = get_wcaps(codec, spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
chans = get_wcaps_channels(chans);
- info->name = get_hdmi_pcm_name(0);
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = get_pin(spec, 0)->pcm_name;
+ sprintf(info->name, "HDMI 0");
info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
*pstr = spec->pcm_playback;
- pstr->nid = spec->cvts[0].cvt_nid;
+ pstr->nid = per_cvt->cvt_nid;
if (pstr->channels_max <= 2 && chans && chans <= 16)
pstr->channels_max = chans;
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
return 0;
}
static int simple_playback_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
int err;
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvts[0].cvt_nid,
- spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ err = snd_hda_create_spdif_out_ctls(codec, per_cvt->cvt_nid,
+ per_cvt->cvt_nid);
if (err < 0)
return err;
return simple_hdmi_build_jack(codec, 0);
static int simple_playback_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin = spec->pins[0].pin_nid;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+ hda_nid_t pin = per_pin->pin_nid;
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
{
struct hdmi_spec *spec = codec->spec;
+ hdmi_array_free(spec);
kfree(spec);
}
hda_nid_t cvt_nid, hda_nid_t pin_nid)
{
struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ hdmi_array_init(spec, 1);
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = cvt_nid;
spec->num_cvts = 1;
spec->num_pins = 1;
- spec->cvts[0].cvt_nid = cvt_nid;
- spec->pins[0].pin_nid = pin_nid;
+ per_pin = snd_array_new(&spec->pins);
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_pin || !per_cvt) {
+ simple_playback_free(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
spec->pcm_playback = simple_pcm_playback;
codec->patch_ops = simple_hdmi_patch_ops;
int i;
struct hdmi_spec *spec = codec->spec;
struct hda_spdif_out *spdif;
+ struct hdmi_spec_per_cvt *per_cvt;
mutex_lock(&codec->spdif_mutex);
- spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
chs = substream->runtime->channels;
{
struct hdmi_spec *spec = codec->spec;
int err = simple_playback_build_pcms(codec);
- spec->pcm_rec[0].own_chmap = true;
+ if (!err) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+ info->own_chmap = true;
+ }
return err;
}
static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
struct snd_pcm_chmap *chmap;
int err;
return err;
/* add channel maps */
- err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm,
+ info = get_pcm_rec(spec, 0);
+ err = snd_pcm_add_chmap_ctls(info->pcm,
SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_alt_chmaps, 8, 0, &chmap);
if (err < 0)
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
int chans = substream->runtime->channels;
int i, err;
substream);
if (err < 0)
return err;
- snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+ snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
/* FIXME: XXX */
for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+ snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
}
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
-#include "hda_beep.h"
#include "hda_jack.h"
#include "hda_generic.h"
ALC_INIT_GPIO3,
};
+enum {
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
+};
+
+enum {
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
+};
+
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
int mute_led_polarity;
hda_nid_t mute_led_nid;
+ unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */
+
+ hda_nid_t headset_mic_pin;
+ hda_nid_t headphone_mic_pin;
+ int current_headset_mode;
+ int current_headset_type;
+
/* hooks */
void (*init_hook)(struct hda_codec *codec);
#ifdef CONFIG_PM
snd_hda_shutup_pins(codec);
}
-static void alc_free(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- snd_hda_gen_spec_free(&spec->gen);
- snd_hda_detach_beep_device(codec);
- kfree(spec);
-}
+#define alc_free snd_hda_gen_free
#ifdef CONFIG_PM
static void alc_power_eapd(struct hda_codec *codec)
spec = codec->spec;
spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
alc880_fixups);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
codec->patch_ops.unsol_event = alc880_unsol_event;
ALC260_FIXUP_HP_B1900,
ALC260_FIXUP_KN1,
ALC260_FIXUP_FSC_S7020,
+ ALC260_FIXUP_FSC_S7020_JWSE,
};
static void alc260_gpio1_automute(struct hda_codec *codec)
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- spec->gen.add_out_jack_modes = 1;
- break;
- case HDA_FIXUP_ACT_PROBE:
+ if (action == HDA_FIXUP_ACT_PROBE)
spec->init_amp = ALC_INIT_NONE;
- break;
+}
+
+static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
}
}
.type = HDA_FIXUP_FUNC,
.v.func = alc260_fixup_fsc_s7020,
},
+ [ALC260_FIXUP_FSC_S7020_JWSE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020_jwse,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_FSC_S7020,
+ },
};
static const struct snd_pci_quirk alc260_fixup_tbl[] = {
{}
};
+static const struct hda_model_fixup alc260_fixup_models[] = {
+ {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC260_FIXUP_COEF, .name = "coef"},
+ {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"},
+ {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"},
+ {}
+};
+
/*
*/
static int patch_alc260(struct hda_codec *codec)
* it's almost harmless.
*/
spec->gen.prefer_hp_amp = 1;
+ spec->gen.beep_nid = 0x01;
- snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
alc_auto_parse_customize_define(codec);
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
/* automatic parse from the BIOS config */
err = alc882_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
alc_auto_parse_customize_define(codec);
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
/* automatic parse from the BIOS config */
err = alc262_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
static int alc268_parse_auto_config(struct hda_codec *codec)
{
static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 };
- struct alc_spec *spec = codec->spec;
- int err = alc_parse_auto_config(codec, NULL, alc268_ssids);
- if (err > 0) {
- if (!spec->gen.no_analog &&
- spec->gen.autocfg.speaker_pins[0] != 0x1d) {
- add_mixer(spec, alc268_beep_mixer);
- snd_hda_add_verbs(codec, alc268_beep_init_verbs);
- }
- }
- return err;
+ return alc_parse_auto_config(codec, NULL, alc268_ssids);
}
/*
static int patch_alc268(struct hda_codec *codec)
{
struct alc_spec *spec;
- int i, has_beep, err;
+ int err;
/* ALC268 has no aa-loopback mixer */
err = alc_alloc_spec(codec, 0);
return err;
spec = codec->spec;
+ spec->gen.beep_nid = 0x01;
snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
if (err < 0)
goto error;
- has_beep = 0;
- for (i = 0; i < spec->num_mixers; i++) {
- if (spec->mixers[i] == alc268_beep_mixer) {
- has_beep = 1;
- break;
- }
- }
-
- if (has_beep) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (err > 0 && !spec->gen.no_analog &&
+ spec->gen.autocfg.speaker_pins[0] != 0x1d) {
+ add_mixer(spec, alc268_beep_mixer);
+ snd_hda_add_verbs(codec, alc268_beep_init_verbs);
if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
/* override the amp caps for beep generator */
snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
};
unsigned int cfg;
- if (strcmp(codec->chip_name, "ALC271X"))
+ if (strcmp(codec->chip_name, "ALC271X") &&
+ strcmp(codec->chip_name, "ALC269VB"))
return;
cfg = snd_hda_codec_get_pincfg(codec, 0x12);
if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
spec->gen.automute_hook = alc269_quanta_automute;
}
+static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+ msleep(500);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
+}
+
+
/* update mute-LED according to the speaker mute state via mic VREF pin */
static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
{
}
}
+/* turn on/off mute LED per vmaster hook */
+static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
+
+ if (enabled)
+ spec->gpio_led &= ~0x08;
+ else
+ spec->gpio_led |= 0x08;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+/* turn on/off mic-mute LED per capture hook */
+static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
+
+ if (!ucontrol)
+ return;
+
+ if (ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1])
+ spec->gpio_led &= ~0x10;
+ else
+ spec->gpio_led |= 0x10;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ snd_hda_add_verbs(codec, gpio_init);
+ }
+}
+
+static void alc_headset_mode_unplugged(struct hda_codec *codec)
+{
+ int val;
+
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x1b, 0x0c0b);
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ val = alc_read_coef_idx(codec, 0x35);
+ alc_write_coef_idx(codec, 0x35, val & 0xbfff);
+ alc_write_coef_idx(codec, 0x06, 0x2104);
+ alc_write_coef_idx(codec, 0x1a, 0x0001);
+ alc_write_coef_idx(codec, 0x26, 0x0004);
+ alc_write_coef_idx(codec, 0x32, 0x42a3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x76, 0x000e);
+ alc_write_coef_idx(codec, 0x6c, 0x2400);
+ alc_write_coef_idx(codec, 0x18, 0x7308);
+ alc_write_coef_idx(codec, 0x6b, 0xc429);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x15, 0x0d40);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ break;
+ }
+ snd_printdd("Headset jack set to unplugged mode.\n");
+}
+
+
+static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
+ hda_nid_t mic_pin)
+{
+ int val;
+
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ val = alc_read_coef_idx(codec, 0x35);
+ alc_write_coef_idx(codec, 0x35, val | 1<<14);
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+ alc_write_coef_idx(codec, 0x1a, 0x0021);
+ alc_write_coef_idx(codec, 0x26, 0x008c);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0292:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_write_coef_idx(codec, 0x19, 0xa208);
+ alc_write_coef_idx(codec, 0x2e, 0xacf0);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ alc_write_coef_idx(codec, 0xb5, 0x1040);
+ val = alc_read_coef_idx(codec, 0xc3);
+ alc_write_coef_idx(codec, 0xc3, val | 1<<12);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ }
+ snd_printdd("Headset jack set to mic-in mode.\n");
+}
+
+static void alc_headset_mode_default(struct hda_codec *codec)
+{
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x76, 0x000e);
+ alc_write_coef_idx(codec, 0x6c, 0x2400);
+ alc_write_coef_idx(codec, 0x6b, 0xc429);
+ alc_write_coef_idx(codec, 0x18, 0x7308);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0041);
+ alc_write_coef_idx(codec, 0x15, 0x0d40);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ break;
+ }
+ snd_printdd("Headset jack set to headphone (default) mode.\n");
+}
+
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd429);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ alc_write_coef_idx(codec, 0x76, 0x0008);
+ alc_write_coef_idx(codec, 0x18, 0x7388);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x15, 0x0d60);
+ alc_write_coef_idx(codec, 0xc3, 0x0000);
+ break;
+ }
+ snd_printdd("Headset jack set to iPhone-style headset mode.\n");
+}
+
+/* Nokia type */
+static void alc_headset_mode_omtp(struct hda_codec *codec)
+{
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xe429);
+ alc_write_coef_idx(codec, 0x1b, 0x0c2b);
+ alc_write_coef_idx(codec, 0x32, 0x4ea3);
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xe429);
+ alc_write_coef_idx(codec, 0x76, 0x0008);
+ alc_write_coef_idx(codec, 0x18, 0x7388);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x15, 0x0d50);
+ alc_write_coef_idx(codec, 0xc3, 0x0000);
+ break;
+ }
+ snd_printdd("Headset jack set to Nokia-style headset mode.\n");
+}
+
+static void alc_determine_headset_type(struct hda_codec *codec)
+{
+ int val;
+ bool is_ctia = false;
+ struct alc_spec *spec = codec->spec;
+
+ switch (codec->vendor_id) {
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd029);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x6c);
+ is_ctia = (val & 0x001c) == 0x001c;
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ alc_write_coef_idx(codec, 0xb7, 0x802b);
+ alc_write_coef_idx(codec, 0x15, 0x0d60);
+ alc_write_coef_idx(codec, 0xc3, 0x0c00);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0xbe);
+ is_ctia = (val & 0x1c02) == 0x1c02;
+ break;
+ }
+
+ snd_printdd("Headset jack detected iPhone-style headset: %s\n",
+ is_ctia ? "yes" : "no");
+ spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
+}
+
+static void alc_update_headset_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+
+ int new_headset_mode;
+
+ if (!snd_hda_jack_detect(codec, hp_pin))
+ new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED;
+ else if (mux_pin == spec->headset_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_HEADSET;
+ else if (mux_pin == spec->headphone_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_MIC;
+ else
+ new_headset_mode = ALC_HEADSET_MODE_HEADPHONE;
+
+ if (new_headset_mode == spec->current_headset_mode)
+ return;
+
+ switch (new_headset_mode) {
+ case ALC_HEADSET_MODE_UNPLUGGED:
+ alc_headset_mode_unplugged(codec);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADSET:
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN)
+ alc_determine_headset_type(codec);
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA)
+ alc_headset_mode_ctia(codec);
+ else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP)
+ alc_headset_mode_omtp(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ case ALC_HEADSET_MODE_MIC:
+ alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADPHONE:
+ alc_headset_mode_default(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ }
+ if (new_headset_mode != ALC_HEADSET_MODE_MIC) {
+ snd_hda_set_pin_ctl_cache(codec, hp_pin,
+ AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ if (spec->headphone_mic_pin)
+ snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin,
+ PIN_VREFHIZ);
+ }
+ spec->current_headset_mode = new_headset_mode;
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+static void alc_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ alc_update_headset_mode(codec);
+}
+
+static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->current_headset_type = ALC_HEADSET_MODE_UNKNOWN;
+ snd_hda_gen_hp_automute(codec, jack);
+}
+
+static void alc_probe_headset_mode(struct hda_codec *codec)
+{
+ int i;
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+ /* Find mic pins */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin)
+ spec->headset_mic_pin = cfg->inputs[i].pin;
+ if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin)
+ spec->headphone_mic_pin = cfg->inputs[i].pin;
+ }
+
+ spec->gen.cap_sync_hook = alc_update_headset_mode_hook;
+ spec->gen.automute_hook = alc_update_headset_mode;
+ spec->gen.hp_automute_hook = alc_update_headset_jack_cb;
+}
+
+static void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ alc_probe_headset_mode(codec);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ spec->current_headset_mode = 0;
+ alc_update_headset_mode(codec);
+ break;
+ }
+}
+
+static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ int val;
+ alc_write_coef_idx(codec, 0xc4, 0x8000);
+ val = alc_read_coef_idx(codec, 0xc2);
+ alc_write_coef_idx(codec, 0xc2, val & 0xfe);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
ALC269_FIXUP_HP_MUTE_LED,
ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_HP_GPIO_LED,
ALC269_FIXUP_INV_DMIC,
ALC269_FIXUP_LENOVO_DOCK,
ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
+ ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC269_FIXUP_HEADSET_MODE,
+ ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC269_FIXUP_ASUS_X101_FUNC,
+ ALC269_FIXUP_ASUS_X101_VERB,
+ ALC269_FIXUP_ASUS_X101,
ALC271_FIXUP_AMIC_MIC2,
ALC271_FIXUP_HP_GATE_MIC_JACK,
+ ALC269_FIXUP_ACER_AC700,
};
static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_hp_mute_led_mic2,
},
+ [ALC269_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_led,
+ },
[ALC269_FIXUP_INV_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_pincfg_no_hp_to_lineout,
},
+ [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21014020 }, /* dock line out */
+ { 0x19, 0x21a19030 }, /* dock mic */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_no_hp_mic,
+ },
+ [ALC269_FIXUP_ASUS_X101_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_x101_headset_mic,
+ },
+ [ALC269_FIXUP_ASUS_X101_VERB] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0310},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_FUNC
+ },
+ [ALC269_FIXUP_ASUS_X101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x04a1182c }, /* Headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_VERB
+ },
[ALC271_FIXUP_AMIC_MIC2] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
.chained = true,
.chain_id = ALC271_FIXUP_AMIC_MIC2,
},
+ [ALC269_FIXUP_ACER_AC700] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x03a11c20 }, /* mic */
+ { 0x1e, 0x0346101e }, /* SPDIF1 */
+ { 0x21, 0x0321101f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC271_FIXUP_DMIC,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c7, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ea, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05eb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ec, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ed, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05ee, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f3, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
{.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
{.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
{.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
+ {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
{}
};
alc_auto_parse_customize_define(codec);
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
switch (codec->vendor_id) {
case 0x10ec0269:
spec->codec_variant = ALC269_TYPE_ALC269VA;
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid)
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM
return err;
spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
- }
codec->patch_ops = alc_patch_ops;
#ifdef CONFIG_PM
return err;
spec = codec->spec;
+ spec->gen.beep_nid = 0x23;
snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- }
codec->patch_ops = alc_patch_ops;
ALC662_FIXUP_NO_JACK_DETECT,
ALC662_FIXUP_ZOTAC_Z68,
ALC662_FIXUP_INV_DMIC,
+ ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC668_FIXUP_HEADSET_MODE,
};
static const struct hda_fixup alc662_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
+ [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MODE
+ },
+ [ALC668_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
alc_auto_parse_customize_define(codec);
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
if ((alc_get_coef0(codec) & (1 << 14)) &&
codec->bus->pci->subsystem_vendor == 0x1025 &&
spec->cdefine.platform_type == 1) {
- if (alc_codec_rename(codec, "ALC272X") < 0)
+ err = alc_codec_rename(codec, "ALC272X");
+ if (err < 0)
goto error;
}
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0)
- goto error;
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
switch (codec->vendor_id) {
case 0x10ec0662:
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
/* beep widgets */
hda_nid_t anabeep_nid;
- hda_nid_t digbeep_nid;
/* SPDIF-out mux */
const char * const *spdif_labels;
{
struct sigmatel_spec *spec = codec->spec;
int err;
+ int flags = 0;
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (spec->headset_jack)
+ flags |= HDA_PINCFG_HEADSET_MIC;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags);
if (err < 0)
return err;
/* setup digital beep controls and input device */
#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->digbeep_nid > 0) {
- hda_nid_t nid = spec->digbeep_nid;
+ if (spec->gen.beep_nid) {
+ hda_nid_t nid = spec->gen.beep_nid;
unsigned int caps;
err = stac_auto_create_beep_ctls(codec, nid);
- if (err < 0)
- return err;
- err = snd_hda_attach_beep_device(codec, nid);
if (err < 0)
return err;
if (codec->beep) {
~spec->eapd_mask);
}
-static void stac_free(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- snd_hda_gen_spec_free(&spec->gen);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
+#define stac_free snd_hda_gen_free
#ifdef CONFIG_PROC_FS
static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
spec->gen.own_eapd_ctl = 1;
codec->patch_ops = stac_patch_ops;
+ codec->power_filter = snd_hda_codec_eapd_power_filter;
snd_hda_add_verbs(codec, stac9200_eapd_init);
spec->aloopback_mask = 0x01;
spec->aloopback_shift = 8;
- spec->digbeep_nid = 0x1c;
+ spec->gen.beep_nid = 0x1c; /* digital beep */
/* GPIO0 High = Enable EAPD */
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
spec->gen.power_down_unused = 1;
spec->gen.mixer_nid = 0x1b;
- spec->digbeep_nid = 0x21;
+ spec->gen.beep_nid = 0x21; /* digital beep */
spec->pwr_nids = stac92hd83xxx_pwr_nids;
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
spec->default_polarity = -1; /* no default cfg */
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
- spec->digbeep_nid = 0x19;
+ spec->gen.beep_nid = 0x19; /* digital beep */
spec->pwr_nids = stac92hd95_pwr_nids;
spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
spec->default_polarity = -1; /* no default cfg */
spec->aloopback_shift = 0;
spec->powerdown_adcs = 1;
- spec->digbeep_nid = 0x26;
+ spec->gen.beep_nid = 0x26; /* digital beep */
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pwr_nids = stac92hd71bxx_pwr_nids;
spec->have_spdif_mux = 1;
spec->spdif_labels = stac927x_spdif_labels;
- spec->digbeep_nid = 0x23;
+ spec->gen.beep_nid = 0x23; /* digital beep */
/* GPIO0 High = Enable EAPD */
spec->eapd_mask = spec->gpio_mask = 0x01;
spec->gen.own_eapd_ctl = 1;
spec->have_spdif_mux = 1;
- spec->digbeep_nid = 0x23;
+ spec->gen.beep_nid = 0x23; /* digital beep */
snd_hda_add_verbs(codec, stac9205_core_init);
spec->aloopback_ctl = &stac9205_loopback;
}
}
+static const struct badness_table via_main_out_badness = {
+ .no_primary_dac = 0x10000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x10000,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x20,
+};
+static const struct badness_table via_extra_out_badness = {
+ .no_primary_dac = 0x4000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x12,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x10,
+};
+
static int via_parse_auto_config(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int err;
+ spec->gen.main_out_badness = &via_main_out_badness;
+ spec->gen.extra_out_badness = &via_extra_out_badness;
+
err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
if (err < 0)
return err;
struct hdspm *hdspm);
static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
static int hdspm_autosync_ref(struct hdspm *hdspm);
static int snd_hdspm_set_defaults(struct hdspm *hdspm);
return ret;
}
+/* round arbitary sample rates to commonly known rates */
+static int hdspm_round_frequency(int rate)
+{
+ if (rate < 38050)
+ return 32000;
+ if (rate < 46008)
+ return 44100;
+ else
+ return 48000;
+}
+
+static int hdspm_tco_sync_check(struct hdspm *hdspm);
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
+
/* check for external sample rate */
static int hdspm_external_sample_rate(struct hdspm *hdspm)
{
break;
}
- /* QS and DS rates normally can not be detected
- * automatically by the card. Only exception is MADI
- * in 96k frame mode.
- *
- * So if we read SS values (32 .. 48k), check for
- * user-provided DS/QS bits in the control register
- * and multiply the base frequency accordingly.
- */
- if (rate <= 48000) {
- if (hdspm->control_register & HDSPM_QuadSpeed)
- rate *= 4;
- else if (hdspm->control_register &
- HDSPM_DoubleSpeed)
- rate *= 2;
+ } /* endif HDSPM_madiLock */
+
+ /* check sample rate from TCO or SYNC_IN */
+ {
+ bool is_valid_input = 0;
+ bool has_sync = 0;
+
+ syncref = hdspm_autosync_ref(hdspm);
+ if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_tco_sync_check(hdspm));
+ } else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_sync_in_sync_check(hdspm));
+ }
+
+ if (is_valid_input && has_sync) {
+ rate = hdspm_round_frequency(
+ hdspm_get_pll_freq(hdspm));
}
}
+
+ /* QS and DS rates normally can not be detected
+ * automatically by the card. Only exception is MADI
+ * in 96k frame mode.
+ *
+ * So if we read SS values (32 .. 48k), check for
+ * user-provided DS/QS bits in the control register
+ * and multiply the base frequency accordingly.
+ */
+ if (rate <= 48000) {
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ rate *= 4;
+ else if (hdspm->control_register &
+ HDSPM_DoubleSpeed)
+ rate *= 2;
+ }
break;
}
/* get the system sample rate which is set */
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm)
+{
+ unsigned int period, rate;
+
+ period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+ rate = hdspm_calc_dds_value(hdspm, period);
+
+ return rate;
+}
+
/**
* Calculate the real sample rate from the
* current DDS value.
**/
static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
{
- unsigned int period, rate;
+ unsigned int rate;
- period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
- rate = hdspm_calc_dds_value(hdspm, period);
+ rate = hdspm_get_pll_freq(hdspm);
if (rate > 207000) {
/* Unreasonable high sample rate as seen on PCI MADI cards. */
return (status >> (idx*4)) & 0xF;
}
+#define ENUMERATED_CTL_INFO(info, texts) \
+{ \
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \
+ uinfo->count = 1; \
+ uinfo->value.enumerated.items = ARRAY_SIZE(texts); \
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; \
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \
+}
+
#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts_freq[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts_freq);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Master", "AutoSync" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
return 0;
}
+
+
+#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_video_input_format, \
+ .get = snd_hdspm_get_tco_video_input_format, \
+}
+
+static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = {"No video", "NTSC", "PAL"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 status;
+ int ret = 0;
+
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
+ HDSPM_TCO1_Video_Input_Format_PAL)) {
+ case HDSPM_TCO1_Video_Input_Format_NTSC:
+ /* ntsc */
+ ret = 1;
+ break;
+ case HDSPM_TCO1_Video_Input_Format_PAL:
+ /* pal */
+ ret = 2;
+ break;
+ default:
+ /* no video */
+ ret = 0;
+ break;
+ }
+ ucontrol->value.enumerated.item[0] = ret;
+ return 0;
+}
+
+
+
+#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_ltc_frames, \
+ .get = snd_hdspm_get_tco_ltc_frames, \
+}
+
+static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
+ "30 fps"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
+{
+ u32 status;
+ int ret = 0;
+
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ if (status & HDSPM_TCO1_LTC_Input_valid) {
+ switch (status & (HDSPM_TCO1_LTC_Format_LSB |
+ HDSPM_TCO1_LTC_Format_MSB)) {
+ case 0:
+ /* 24 fps */
+ ret = 1;
+ break;
+ case HDSPM_TCO1_LTC_Format_LSB:
+ /* 25 fps */
+ ret = 2;
+ break;
+ case HDSPM_TCO1_LTC_Format_MSB:
+ /* 25 fps */
+ ret = 3;
+ break;
+ default:
+ /* 30 fps */
+ ret = 4;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
+ return 0;
+}
+
#define HDSPM_TOGGLE_SETTING(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "optical", "coaxial" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double", "Quad" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double", "Quad" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
.get = snd_hdspm_get_sync_check \
}
+#define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_tco_info_lock_check, \
+ .get = snd_hdspm_get_sync_check \
+}
+
+
static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "No Lock", "Lock" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
return 0;
}
+static int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
+{
+ u32 status;
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+
+ return (status & mask) ? 1 : 0;
+}
+
static int hdspm_tco_sync_check(struct hdspm *hdspm)
{
}
+ if (hdspm->tco) {
+ switch (kcontrol->private_value) {
+ case 11:
+ /* Check TCO for lock state of its current input */
+ val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
+ break;
+ case 12:
+ /* Check TCO for valid time code on LTC input. */
+ val = hdspm_tco_input_check(hdspm,
+ HDSPM_TCO1_LTC_Input_valid);
+ break;
+ default:
+ break;
+ }
+ }
+
if (-1 == val)
val = 3;
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "44.1 kHz", "48 kHz" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
{
static char *texts[] = { "24 fps", "25 fps", "29.97fps",
"29.97 dfps", "30 fps", "30 dfps" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 6;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "LTC", "Video", "WCK" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
+ HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
HDSPM_INPUT_SELECT("Input Select", 0),
HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
- HDSPM_TCO_WORD_TERM("TCO Word Term", 0)
+ HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
+ HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
+ HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
+ HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
+ HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
};
break;
case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ /*
+ * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks.
+ *
+ * The SSC transmit clock is obtained from the BCLK signal on
+ * on the TK line, and the SSC receive clock is
+ * generated from the transmit clock.
+ *
+ * Data is transferred on first BCLK after LRC pulse rising
+ * edge.If stereo, the right channel data is contiguous with
+ * the left channel data.
+ */
+ rcmr = SSC_BF(RCMR_PERIOD, 0)
+ | SSC_BF(RCMR_STTDLY, START_DELAY)
+ | SSC_BF(RCMR_START, SSC_START_RISING_RF)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(RCMR_CKS, SSC_CKS_PIN);
+
+ rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
+ | SSC_BF(RFMR_FSLEN, 0)
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tcmr = SSC_BF(TCMR_PERIOD, 0)
+ | SSC_BF(TCMR_STTDLY, START_DELAY)
+ | SSC_BF(TCMR_START, SSC_START_RISING_RF)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+ | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+
+ tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
+ | SSC_BF(TFMR_FSLEN, 0)
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+ break;
+
default:
printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
ssc_p->daifmt);
.ops = &atmel_ssc_dai_ops,
};
+static const struct snd_soc_component_driver atmel_ssc_component = {
+ .name = "atmel-ssc",
+};
+
static int asoc_ssc_init(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct ssc_device *ssc = platform_get_drvdata(pdev);
int ret;
- ret = snd_soc_register_dai(dev, &atmel_ssc_dai);
+ ret = snd_soc_register_component(dev, &atmel_ssc_component,
+ &atmel_ssc_dai, 1);
if (ret) {
dev_err(dev, "Could not register DAI: %d\n", ret);
goto err;
return 0;
err_unregister_dai:
- snd_soc_unregister_dai(dev);
+ snd_soc_unregister_component(dev);
err:
return ret;
}
else
atmel_pcm_pdc_platform_unregister(dev);
- snd_soc_unregister_dai(dev);
+ snd_soc_unregister_component(dev);
}
/**
.ops = &alchemy_ac97c_ops,
};
+static const struct snd_soc_component_driver au1xac97c_component = {
+ .name = "au1xac97c",
+};
+
static int au1xac97c_drvprobe(struct platform_device *pdev)
{
int ret;
platform_set_drvdata(pdev, ctx);
- ret = snd_soc_register_dai(&pdev->dev, &au1xac97c_dai_driver);
+ ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component,
+ &au1xac97c_dai_driver, 1);
if (ret)
return ret;
{
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
.ops = &au1xi2s_dai_ops,
};
+static const struct snd_soc_component_driver au1xi2s_component = {
+ .name = "au1xi2s",
+};
+
static int au1xi2s_drvprobe(struct platform_device *pdev)
{
struct resource *iores, *dmares;
platform_set_drvdata(pdev, ctx);
- return snd_soc_register_dai(&pdev->dev, &au1xi2s_dai_driver);
+ return snd_soc_register_component(&pdev->dev, &au1xi2s_component,
+ &au1xi2s_dai_driver, 1);
}
static int au1xi2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
.ops = &au1xpsc_ac97_dai_ops,
};
+static const struct snd_soc_component_driver au1xpsc_ac97_component = {
+ .name = "au1xpsc-ac97",
+};
+
static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
int ret;
platform_set_drvdata(pdev, wd);
- ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
+ ret = snd_soc_register_component(&pdev->dev, &au1xpsc_ac97_component,
+ &wd->dai_drv, 1);
if (ret)
return ret;
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
/* disable PSC completely */
au_writel(0, AC97_CFG(wd));
.ops = &au1xpsc_i2s_dai_ops,
};
+static const struct snd_soc_component_driver au1xpsc_i2s_component = {
+ .name = "au1xpsc-i2s",
+};
+
static int au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
struct resource *iores, *dmares;
platform_set_drvdata(pdev, wd);
- return snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
+ return snd_soc_register_component(&pdev->dev, &au1xpsc_i2s_component,
+ &wd->dai_drv, 1);
}
static int au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
au_writel(0, I2S_CFG(wd));
au_sync();
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
+static const struct snd_soc_component_driver bfin_ac97_component = {
+ .name = "bfin-ac97",
+};
+
static int asoc_bfin_ac97_probe(struct platform_device *pdev)
{
struct sport_device *sport_handle;
goto sport_config_err;
}
- ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
+ ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component,
+ &bfin_ac97_dai, 1);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
.ops = &bf5xx_i2s_dai_ops,
};
+static const struct snd_soc_component_driver bf5xx_i2s_component = {
+ .name = "bf5xx-i2s",
+};
+
static int bf5xx_i2s_probe(struct platform_device *pdev)
{
struct sport_device *sport_handle;
return -ENODEV;
/* register with the ASoC layers */
- ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
+ &bf5xx_i2s_dai, 1);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
pr_debug("%s enter\n", __func__);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
return 0;
.ops = &bf5xx_tdm_dai_ops,
};
+static const struct snd_soc_component_driver bf5xx_tdm_component = {
+ .name = "bf5xx-tdm",
+};
+
static int bfin_tdm_probe(struct platform_device *pdev)
{
struct sport_device *sport_handle;
goto sport_config_err;
}
- ret = snd_soc_register_dai(&pdev->dev, &bf5xx_tdm_dai);
+ ret = snd_soc_register_component(&pdev->dev, &bf5xx_tdm_component,
+ &bf5xx_tdm_dai, 1);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
sport_done(sport_handle);
return 0;
.ops = &bfin_i2s_dai_ops,
};
+static const struct snd_soc_component_driver bfin_i2s_component = {
+ .name = "bfin-i2s",
+};
+
static int bfin_i2s_probe(struct platform_device *pdev)
{
struct sport_device *sport;
return -ENODEV;
/* register with the ASoC layers */
- ret = snd_soc_register_dai(dev, &bfin_i2s_dai);
+ ret = snd_soc_register_component(dev, &bfin_i2s_component,
+ &bfin_i2s_dai, 1);
if (ret) {
dev_err(dev, "Failed to register DAI: %d\n", ret);
sport_delete(sport);
{
struct sport_device *sport = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
sport_delete(sport);
return 0;
.ops = &ep93xx_ac97_dai_ops,
};
+static const struct snd_soc_component_driver ep93xx_ac97_component = {
+ .name = "ep93xx-ac97",
+};
+
static int ep93xx_ac97_probe(struct platform_device *pdev)
{
struct ep93xx_ac97_info *info;
ep93xx_ac97_info = info;
platform_set_drvdata(pdev, info);
- ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
+ ret = snd_soc_register_component(&pdev->dev, &ep93xx_ac97_component,
+ &ep93xx_ac97_dai, 1);
if (ret)
goto fail;
{
struct ep93xx_ac97_info *info = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
/* disable the AC97 controller */
ep93xx_ac97_write_reg(info, AC97GCR, 0);
.ops = &ep93xx_i2s_dai_ops,
};
+static const struct snd_soc_component_driver ep93xx_i2s_component = {
+ .name = "ep93xx-i2s",
+};
+
static int ep93xx_i2s_probe(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info;
dev_set_drvdata(&pdev->dev, info);
info->dma_data = ep93xx_i2s_dma_data;
- err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
+ err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component,
+ &ep93xx_i2s_dai, 1);
if (err)
goto fail_put_lrclk;
{
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk);
clk_put(info->sclk);
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
+ select SND_SOC_AK5386
select SND_SOC_ALC5623 if I2C
select SND_SOC_ALC5632 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_STA32X if I2C
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
+ select SND_SOC_TAS5086 if I2C
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC32X4 if I2C
config SND_SOC_AK4671
tristate
+config SND_SOC_AK5386
+ tristate
+
config SND_SOC_ALC5623
tristate
config SND_SOC_ALC5632
config SND_SOC_STAC9766
tristate
+config SND_SOC_TAS5086
+ tristate
+
config SND_SOC_TLV320AIC23
tristate
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
+snd-soc-ak5386-objs := ak5386.o
snd-soc-arizona-objs := arizona.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-sta529-objs := sta529.o
snd-soc-stac9766-objs := stac9766.o
+snd-soc-tas5086-objs := tas5086.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
#define ADAU1373_DAI_FORMAT_DSP 0x3
#define ADAU1373_BCLKDIV_SOURCE BIT(5)
+#define ADAU1373_BCLKDIV_SR_MASK (0x07 << 2)
+#define ADAU1373_BCLKDIV_BCLK_MASK 0x03
#define ADAU1373_BCLKDIV_32 0x03
#define ADAU1373_BCLKDIV_64 0x02
#define ADAU1373_BCLKDIV_128 0x01
adau1373_dai->enable_src = (div != 0);
snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id),
- ~ADAU1373_BCLKDIV_SOURCE, (div << 2) | ADAU1373_BCLKDIV_64);
+ ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK,
+ (div << 2) | ADAU1373_BCLKDIV_64);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
int val = 0;
int ret;
if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
- ret = snd_soc_update_bits(codec, AK4104_REG_CONTROL1,
- AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1,
- val);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1,
+ val);
if (ret < 0)
return ret;
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- int val = 0;
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ int ret, val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
val |= IEC958_AES0_CON_NOT_COPYRIGHT;
- snd_soc_write(codec, AK4104_REG_CHN_STATUS(0), val);
+ regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(0), val);
val = 0;
return -EINVAL;
}
- return snd_soc_write(codec, AK4104_REG_CHN_STATUS(3), val);
+ ret = regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(3), val);
+ if (ret < 0)
+ return ret;
+
+ /* enable transmitter */
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
+ AK4104_TX_TXE, AK4104_TX_TXE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ak4104_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+
+ /* disable transmitter */
+ return regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
+ AK4104_TX_TXE, 0);
}
static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
+ .hw_free = ak4104_hw_free,
.set_fmt = ak4104_set_dai_fmt,
};
int ret;
codec->control_data = ak4104->regmap;
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
- if (ret != 0)
- return ret;
/* set power-up and non-reset bits */
- ret = snd_soc_update_bits(codec, AK4104_REG_CONTROL1,
- AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
- AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
if (ret < 0)
return ret;
/* enable transmitter */
- ret = snd_soc_update_bits(codec, AK4104_REG_TX,
- AK4104_TX_TXE, AK4104_TX_TXE);
+ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
+ AK4104_TX_TXE, AK4104_TX_TXE);
if (ret < 0)
return ret;
static int ak4104_remove(struct snd_soc_codec *codec)
{
- snd_soc_update_bits(codec, AK4104_REG_CONTROL1,
- AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
+ struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
+ AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
return 0;
}
--- /dev/null
+/*
+ * ALSA SoC driver for
+ * Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC
+ *
+ * (c) 2013 Daniel Mack <zonque@gmail.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/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+struct ak5386_priv {
+ int reset_gpio;
+};
+
+static struct snd_soc_codec_driver soc_codec_ak5386;
+
+static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ format &= SND_SOC_DAIFMT_FORMAT_MASK;
+ if (format != SND_SOC_DAIFMT_LEFT_J &&
+ format != SND_SOC_DAIFMT_I2S) {
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ak5386_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 ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * From the datasheet:
+ *
+ * All external clocks (MCLK, SCLK and LRCK) must be present unless
+ * PDN pin = “L”. If these clocks are not provided, the AK5386 may
+ * draw excess current due to its use of internal dynamically
+ * refreshed logic. If the external clocks are not present, place
+ * the AK5386 in power-down mode (PDN pin = “L”).
+ */
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 1);
+
+ return 0;
+}
+
+static int ak5386_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ gpio_set_value(priv->reset_gpio, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ak5386_dai_ops = {
+ .set_fmt = ak5386_set_dai_fmt,
+ .hw_params = ak5386_hw_params,
+ .hw_free = ak5386_hw_free,
+};
+
+static struct snd_soc_dai_driver ak5386_dai = {
+ .name = "ak5386-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .ops = &ak5386_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ak5386_dt_ids[] = {
+ { .compatible = "asahi-kasei,ak5386", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak5386_dt_ids);
+#endif
+
+static int ak5386_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ak5386_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reset_gpio = -EINVAL;
+ dev_set_drvdata(dev, priv);
+
+ if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
+ priv->reset_gpio = of_get_named_gpio(dev->of_node,
+ "reset-gpio", 0);
+
+ if (gpio_is_valid(priv->reset_gpio))
+ if (devm_gpio_request_one(dev, priv->reset_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "AK5386 Reset"))
+ priv->reset_gpio = -EINVAL;
+
+ return snd_soc_register_codec(dev, &soc_codec_ak5386,
+ &ak5386_dai, 1);
+}
+
+static int ak5386_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver ak5386_driver = {
+ .probe = ak5386_probe,
+ .remove = ak5386_remove,
+ .driver = {
+ .name = "ak5386",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ak5386_dt_ids),
+ },
+};
+
+module_platform_driver(ak5386_driver);
+
+MODULE_DESCRIPTION("ASoC driver for AK5386 ADC");
+MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
+MODULE_LICENSE("GPL");
* published by the Free Software Foundation.
*/
+#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#define arizona_aif_dbg(_dai, fmt, ...) \
dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ bool manual_ena = false;
+ int val;
+
+ switch (arizona->type) {
+ case WM5102:
+ switch (arizona->rev) {
+ case 0:
+ break;
+ default:
+ manual_ena = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!priv->spk_ena && manual_ena) {
+ snd_soc_write(codec, 0x4f5, 0x25a);
+ priv->spk_ena_pending = true;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
+ if (val & ARIZONA_SPK_SHUTDOWN_STS) {
+ dev_crit(arizona->dev,
+ "Speaker not enabled due to temperature\n");
+ return -EBUSY;
+ }
+
+ snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 1 << w->shift);
+
+ if (priv->spk_ena_pending) {
+ msleep(75);
+ snd_soc_write(codec, 0x4f5, 0xda);
+ priv->spk_ena_pending = false;
+ priv->spk_ena++;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ if (manual_ena) {
+ priv->spk_ena--;
+ if (!priv->spk_ena)
+ snd_soc_write(codec, 0x4f5, 0x25a);
+ }
+
+ snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1,
+ 1 << w->shift, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (manual_ena) {
+ if (!priv->spk_ena)
+ snd_soc_write(codec, 0x4f5, 0x0da);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t arizona_thermal_warn(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_SHUTDOWN_WARN_STS) {
+ dev_crit(arizona->dev, "Thermal warning\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_thermal_shutdown(int irq, void *data)
+{
+ struct arizona *arizona = data;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3,
+ &val);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to read thermal status: %d\n",
+ ret);
+ } else if (val & ARIZONA_SPK_SHUTDOWN_STS) {
+ dev_crit(arizona->dev, "Thermal shutdown\n");
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT4L_ENA |
+ ARIZONA_OUT4R_ENA, 0);
+ if (ret != 0)
+ dev_crit(arizona->dev,
+ "Failed to disable speaker outputs: %d\n",
+ ret);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_soc_dapm_widget arizona_spkl =
+ SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM,
+ ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU);
+
+static const struct snd_soc_dapm_widget arizona_spkr =
+ SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM,
+ ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU);
+
+int arizona_init_spk(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1);
+ if (ret != 0)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1);
+ if (ret != 0)
+ return ret;
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN,
+ "Thermal warning", arizona_thermal_warn,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal warning IRQ: %d\n",
+ ret);
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN,
+ "Thermal shutdown", arizona_thermal_shutdown,
+ arizona);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Failed to get thermal shutdown IRQ: %d\n",
+ ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
+ "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
+};
+EXPORT_SYMBOL_GPL(arizona_rate_text);
+
+const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
+ 0, 1, 2, 8,
+};
+EXPORT_SYMBOL_GPL(arizona_rate_val);
+
+
+const struct soc_enum arizona_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2,
+ ARIZONA_ISRC1_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_2,
+ ARIZONA_ISRC2_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_2,
+ ARIZONA_ISRC3_FSL_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+EXPORT_SYMBOL_GPL(arizona_isrc_fsl);
+
static const char *arizona_vol_ramp_text[] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"15ms/6dB", "30ms/6dB",
4, arizona_ng_hold_text);
EXPORT_SYMBOL_GPL(arizona_ng_hold);
+static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+ int i;
+
+ if (ena)
+ val = ARIZONA_IN_VU;
+ else
+ val = 0;
+
+ for (i = 0; i < priv->num_inputs; i++)
+ snd_soc_update_bits(codec,
+ ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4),
+ ARIZONA_IN_VU, val);
+}
+
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(w->codec);
unsigned int reg;
if (w->shift % 2)
reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ priv->in_pending++;
+ break;
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, 0);
+
+ /* If this is the last input pending then allow VU */
+ priv->in_pending--;
+ if (priv->in_pending == 0) {
+ msleep(1);
+ arizona_in_set_vu(w->codec, 1);
+ }
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE,
- ARIZONA_IN1L_MUTE);
+ snd_soc_update_bits(w->codec, reg,
+ ARIZONA_IN1L_MUTE | ARIZONA_IN_VU,
+ ARIZONA_IN1L_MUTE | ARIZONA_IN_VU);
break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable volume updates if no inputs are enabled */
+ reg = snd_soc_read(w->codec, ARIZONA_INPUT_ENABLES);
+ if (reg == 0)
+ arizona_in_set_vu(w->codec, 0);
}
return 0;
struct snd_kcontrol *kcontrol,
int event)
{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ switch (w->shift) {
+ case ARIZONA_OUT1L_ENA_SHIFT:
+ case ARIZONA_OUT1R_ENA_SHIFT:
+ case ARIZONA_OUT2L_ENA_SHIFT:
+ case ARIZONA_OUT2R_ENA_SHIFT:
+ case ARIZONA_OUT3L_ENA_SHIFT:
+ case ARIZONA_OUT3R_ENA_SHIFT:
+ msleep(17);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_out_ev);
+int arizona_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(w->codec);
+ unsigned int mask = 1 << w->shift;
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = mask;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Store the desired state for the HP outputs */
+ priv->arizona->hp_ena &= ~mask;
+ priv->arizona->hp_ena |= val;
+
+ /* Force off if HPDET magic is active */
+ if (priv->arizona->hpdet_magic)
+ val = 0;
+
+ snd_soc_update_bits(w->codec, ARIZONA_OUTPUT_ENABLES_1, mask, val);
+
+ return arizona_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(arizona_hp_ev);
+
static unsigned int arizona_sysclk_48k_rates[] = {
6144000,
12288000,
break;
case 11289600:
case 12288000:
- val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_12MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 22579200:
case 24576000:
- val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_24MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 45158400:
case 49152000:
- val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_49MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 67737600:
case 73728000:
- val |= 4 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_73MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 90316800:
case 98304000:
- val |= 5 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_98MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 135475200:
case 147456000:
- val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ val |= ARIZONA_CLK_147MHZ << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
case 0:
dev_dbg(arizona->dev, "%s cleared\n", name);
struct arizona *arizona = priv->arizona;
int base = dai->driver->base;
const int *rates;
- int i, ret;
+ int i, ret, val;
int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
int bclk, lrclk, wl, frame, bclk_target;
bclk_target *= chan_limit;
}
+ /* Force stereo for I2S mode */
+ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
+ if (params_channels(params) == 1 && (val & ARIZONA_AIF1_FMT_MASK)) {
+ arizona_aif_dbg(dai, "Forcing stereo mode\n");
+ bclk_target *= 2;
+ }
+
for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
if (rates[i] >= bclk_target &&
rates[i] % params_rate(params) == 0) {
{ 1000000, 13500000, 0, 1 },
};
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 gain;
+} fll_gains[] = {
+ { 0, 256000, 0 },
+ { 256000, 1000000, 2 },
+ { 1000000, 13500000, 4 },
+};
+
struct arizona_fll_cfg {
int n;
int theta;
int refdiv;
int outdiv;
int fratio;
+ int gain;
};
static int arizona_calc_fll(struct arizona_fll *fll,
return -EINVAL;
}
+ for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
+ if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
+ cfg->gain = fll_gains[i].gain;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_gains)) {
+ arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
+ Fref);
+ return -EINVAL;
+ }
+
cfg->n = target / (ratio * Fref);
if (target % (ratio * Fref)) {
cfg->n, cfg->theta, cfg->lambda);
arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
+ arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
return 0;
}
static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
- struct arizona_fll_cfg *cfg, int source)
+ struct arizona_fll_cfg *cfg, int source,
+ bool sync)
{
regmap_update_bits(arizona->regmap, base + 3,
ARIZONA_FLL1_THETA_MASK, cfg->theta);
cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
+ if (sync)
+ regmap_update_bits(arizona->regmap, base + 0x7,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+ else
+ regmap_update_bits(arizona->regmap, base + 0x9,
+ ARIZONA_FLL1_GAIN_MASK,
+ cfg->gain << ARIZONA_FLL1_GAIN_SHIFT);
+
regmap_update_bits(arizona->regmap, base + 2,
ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
ARIZONA_FLL1_CTRL_UPD | cfg->n);
}
-int arizona_set_fll(struct arizona_fll *fll, int source,
- unsigned int Fref, unsigned int Fout)
+static bool arizona_is_enabled_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
- struct arizona_fll_cfg cfg, sync;
- unsigned int reg, val;
- int syncsrc;
- bool ena;
+ unsigned int reg;
int ret;
- if (fll->fref == Fref && fll->fout == Fout)
- return 0;
-
ret = regmap_read(arizona->regmap, fll->base + 1, ®);
if (ret != 0) {
arizona_fll_err(fll, "Failed to read current state: %d\n",
ret);
return ret;
}
- ena = reg & ARIZONA_FLL1_ENA;
- if (Fout) {
- /* Do we have a 32kHz reference? */
- regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
- switch (val & ARIZONA_CLK_32K_SRC_MASK) {
- case ARIZONA_CLK_SRC_MCLK1:
- case ARIZONA_CLK_SRC_MCLK2:
- syncsrc = val & ARIZONA_CLK_32K_SRC_MASK;
- break;
- default:
- syncsrc = -1;
- }
+ return reg & ARIZONA_FLL1_ENA;
+}
- if (source == syncsrc)
- syncsrc = -1;
+static void arizona_enable_fll(struct arizona_fll *fll,
+ struct arizona_fll_cfg *ref,
+ struct arizona_fll_cfg *sync)
+{
+ struct arizona *arizona = fll->arizona;
+ int ret;
- if (syncsrc >= 0) {
- ret = arizona_calc_fll(fll, &sync, Fref, Fout);
- if (ret != 0)
- return ret;
+ /*
+ * If we have both REFCLK and SYNCCLK then enable both,
+ * otherwise apply the SYNCCLK settings to REFCLK.
+ */
+ if (fll->ref_src >= 0 && fll->ref_src != fll->sync_src) {
+ regmap_update_bits(arizona->regmap, fll->base + 5,
+ ARIZONA_FLL1_OUTDIV_MASK,
+ ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+
+ arizona_apply_fll(arizona, fll->base, ref, fll->ref_src,
+ false);
+ if (fll->sync_src >= 0)
+ arizona_apply_fll(arizona, fll->base + 0x10, sync,
+ fll->sync_src, true);
+ } else if (fll->sync_src >= 0) {
+ regmap_update_bits(arizona->regmap, fll->base + 5,
+ ARIZONA_FLL1_OUTDIV_MASK,
+ sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+
+ arizona_apply_fll(arizona, fll->base, sync,
+ fll->sync_src, false);
- ret = arizona_calc_fll(fll, &cfg, 32768, Fout);
- if (ret != 0)
- return ret;
- } else {
- ret = arizona_calc_fll(fll, &cfg, Fref, Fout);
- if (ret != 0)
- return ret;
- }
- } else {
- regmap_update_bits(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, 0);
regmap_update_bits(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA, 0);
-
- if (ena)
- pm_runtime_put_autosuspend(arizona->dev);
-
- fll->fref = Fref;
- fll->fout = Fout;
-
- return 0;
- }
-
- regmap_update_bits(arizona->regmap, fll->base + 5,
- ARIZONA_FLL1_OUTDIV_MASK,
- cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
-
- if (syncsrc >= 0) {
- arizona_apply_fll(arizona, fll->base, &cfg, syncsrc);
- arizona_apply_fll(arizona, fll->base + 0x10, &sync, source);
} else {
- arizona_apply_fll(arizona, fll->base, &cfg, source);
+ arizona_fll_err(fll, "No clocks provided\n");
+ return;
}
- if (!ena)
+ /*
+ * Increase the bandwidth if we're not using a low frequency
+ * sync source.
+ */
+ if (fll->sync_src >= 0 && fll->sync_freq > 100000)
+ regmap_update_bits(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW, 0);
+ else
+ regmap_update_bits(arizona->regmap, fll->base + 0x17,
+ ARIZONA_FLL1_SYNC_BW, ARIZONA_FLL1_SYNC_BW);
+
+ if (!arizona_is_enabled_fll(fll))
pm_runtime_get(arizona->dev);
/* Clear any pending completions */
regmap_update_bits(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
- if (syncsrc >= 0)
+ if (fll->ref_src >= 0 && fll->sync_src >= 0 &&
+ fll->ref_src != fll->sync_src)
regmap_update_bits(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
msecs_to_jiffies(250));
if (ret == 0)
arizona_fll_warn(fll, "Timed out waiting for lock\n");
+}
+
+static void arizona_disable_fll(struct arizona_fll *fll)
+{
+ struct arizona *arizona = fll->arizona;
+ bool change;
+
+ regmap_update_bits_check(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_ENA, 0, &change);
+ regmap_update_bits(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0);
+
+ if (change)
+ pm_runtime_put_autosuspend(arizona->dev);
+}
+
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct arizona_fll_cfg ref, sync;
+ int ret;
+
+ if (fll->ref_src == source && fll->ref_freq == Fref)
+ return 0;
+
+ if (fll->fout && Fref > 0) {
+ ret = arizona_calc_fll(fll, &ref, Fref, fll->fout);
+ if (ret != 0)
+ return ret;
+
+ if (fll->sync_src >= 0) {
+ ret = arizona_calc_fll(fll, &sync, fll->sync_freq,
+ fll->fout);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ fll->ref_src = source;
+ fll->ref_freq = Fref;
- fll->fref = Fref;
+ if (fll->fout && Fref > 0) {
+ arizona_enable_fll(fll, &ref, &sync);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
+
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct arizona_fll_cfg ref, sync;
+ int ret;
+
+ if (fll->sync_src == source &&
+ fll->sync_freq == Fref && fll->fout == Fout)
+ return 0;
+
+ if (Fout) {
+ if (fll->ref_src >= 0) {
+ ret = arizona_calc_fll(fll, &ref, fll->ref_freq,
+ Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = arizona_calc_fll(fll, &sync, Fref, Fout);
+ if (ret != 0)
+ return ret;
+ }
+
+ fll->sync_src = source;
+ fll->sync_freq = Fref;
fll->fout = Fout;
+ if (Fout) {
+ arizona_enable_fll(fll, &ref, &sync);
+ } else {
+ arizona_disable_fll(fll);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_set_fll);
int ok_irq, struct arizona_fll *fll)
{
int ret;
+ unsigned int val;
init_completion(&fll->ok);
fll->id = id;
fll->base = base;
fll->arizona = arizona;
+ fll->sync_src = ARIZONA_FLL_SRC_NONE;
+
+ /* Configure default refclk to 32kHz if we have one */
+ regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
+ switch (val & ARIZONA_CLK_32K_SRC_MASK) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ case ARIZONA_CLK_SRC_MCLK2:
+ fll->ref_src = val & ARIZONA_CLK_32K_SRC_MASK;
+ break;
+ default:
+ fll->ref_src = ARIZONA_FLL_SRC_NONE;
+ }
+ fll->ref_freq = 32768;
snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id);
snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
#define ARIZONA_CLK_SRC_AIF2BCLK 0x9
#define ARIZONA_CLK_SRC_AIF3BCLK 0xa
+#define ARIZONA_FLL_SRC_NONE -1
#define ARIZONA_FLL_SRC_MCLK1 0
#define ARIZONA_FLL_SRC_MCLK2 1
#define ARIZONA_FLL_SRC_SLIMCLK 3
#define ARIZONA_MIXER_VOL_SHIFT 1
#define ARIZONA_MIXER_VOL_WIDTH 7
+#define ARIZONA_CLK_6MHZ 0
+#define ARIZONA_CLK_12MHZ 1
+#define ARIZONA_CLK_24MHZ 2
+#define ARIZONA_CLK_49MHZ 3
+#define ARIZONA_CLK_73MHZ 4
+#define ARIZONA_CLK_98MHZ 5
+#define ARIZONA_CLK_147MHZ 6
+
#define ARIZONA_MAX_DAI 4
#define ARIZONA_MAX_ADSP 4
int sysclk;
int asyncclk;
struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
+
+ int num_inputs;
+ unsigned int in_pending;
+
+ unsigned int spk_ena:2;
+ unsigned int spk_ena_pending:1;
};
#define ARIZONA_NUM_MIXER_INPUTS 99
ARIZONA_MIXER_ROUTES(name, name "L"), \
ARIZONA_MIXER_ROUTES(name, name "R")
+#define ARIZONA_RATE_ENUM_SIZE 4
+extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
+extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+
+extern const struct soc_enum arizona_isrc_fsl[];
+
extern const struct soc_enum arizona_in_vi_ramp;
extern const struct soc_enum arizona_in_vd_ramp;
extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
+extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event);
extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir);
unsigned int base;
unsigned int vco_mult;
struct completion ok;
- unsigned int fref;
+
unsigned int fout;
+ int sync_src;
+ unsigned int sync_freq;
+ int ref_src;
+ unsigned int ref_freq;
char lock_name[ARIZONA_FLL_NAME_LEN];
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
extern int arizona_init_fll(struct arizona *arizona, int id, int base,
int lock_irq, int ok_irq, struct arizona_fll *fll);
+extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
extern int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
+extern int arizona_init_spk(struct snd_soc_codec *codec);
+
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
priv->config[id].mmcc &= 0xC0;
priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc;
priv->config[id].spc &= 0xFC;
- priv->config[id].spc |= MCK_SCLK_MCLK;
+ /* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */
+ if (priv->mclk >= 6400000)
+ priv->config[id].spc |= MCK_SCLK_64FS;
+ else
+ priv->config[id].spc |= MCK_SCLK_MCLK;
} else {
/* CS42L73 Slave */
priv->config[id].spc &= 0xFC;
#include <sound/max98090.h>
#include "max98090.h"
-#include <linux/version.h>
-
#define DEBUG
#define EXTMIC_METHOD
#define EXTMIC_METHOD_TEST
return 0;
}
-static const char * max98090_perf_pwr_text[] =
+static const char *max98090_perf_pwr_text[] =
{ "High Performance", "Low Power" };
-static const char * max98090_pwr_perf_text[] =
+static const char *max98090_pwr_perf_text[] =
{ "Low Power", "High Performance" };
static const struct soc_enum max98090_vcmbandgap_enum =
SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT,
ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
-static const char * max98090_osr128_text[] = { "64*fs", "128*fs" };
+static const char *max98090_osr128_text[] = { "64*fs", "128*fs" };
static const struct soc_enum max98090_osr128_enum =
SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT,
M98090_FLT_DMIC34MODE_SHIFT,
ARRAY_SIZE(max98090_mode_text), max98090_mode_text);
-static const char * max98090_drcatk_text[] =
+static const char *max98090_drcatk_text[] =
{ "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" };
static const struct soc_enum max98090_drcatk_enum =
SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT,
ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text);
-static const char * max98090_drcrls_text[] =
+static const char *max98090_drcrls_text[] =
{ "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" };
static const struct soc_enum max98090_drcrls_enum =
SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT,
ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text);
-static const char * max98090_alccmp_text[] =
+static const char *max98090_alccmp_text[] =
{ "1:1", "1:1.5", "1:2", "1:4", "1:INF" };
static const struct soc_enum max98090_alccmp_enum =
SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT,
ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text);
-static const char * max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
+static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
static const struct soc_enum max98090_drcexp_enum =
SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT,
static const struct snd_kcontrol_new max98090_mic2_mux =
SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
-static const char * max98090_micpre_text[] = { "Off", "On" };
+static const char *max98090_micpre_text[] = { "Off", "On" };
static const struct soc_enum max98090_pa1en_enum =
SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
* seen for the case of TDM mode. The remaining cases have
* normal logic.
*/
- if (max98090->tdm_slots > 1) {
+ if (max98090->tdm_slots > 1)
regval ^= M98090_BCI_MASK;
- }
snd_soc_write(codec,
M98090_REG_INTERFACE_FORMAT, regval);
if (!active)
return IRQ_NONE;
- if (active & M98090_CLD_MASK) {
+ if (active & M98090_CLD_MASK)
dev_err(codec->dev, "M98090_CLD_MASK\n");
- }
- if (active & M98090_SLD_MASK) {
+ if (active & M98090_SLD_MASK)
dev_dbg(codec->dev, "M98090_SLD_MASK\n");
- }
- if (active & M98090_ULK_MASK) {
+ if (active & M98090_ULK_MASK)
dev_err(codec->dev, "M98090_ULK_MASK\n");
- }
if (active & M98090_JDET_MASK) {
dev_dbg(codec->dev, "M98090_JDET_MASK\n");
msecs_to_jiffies(100));
}
- if (active & M98090_DRCACT_MASK) {
+ if (active & M98090_DRCACT_MASK)
dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
- }
- if (active & M98090_DRCCLP_MASK) {
+ if (active & M98090_DRCCLP_MASK)
dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
- }
return IRQ_HANDLED;
}
max98090->pdata = i2c->dev.platform_data;
max98090->irq = i2c->irq;
- max98090->regmap = regmap_init_i2c(i2c, &max98090_regmap);
+ max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
if (IS_ERR(max98090->regmap)) {
ret = PTR_ERR(max98090->regmap);
dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_max98090, max98090_dai,
ARRAY_SIZE(max98090_dai));
- if (ret < 0)
- regmap_exit(max98090->regmap);
-
err_enable:
return ret;
}
static int max98090_i2c_remove(struct i2c_client *client)
{
- struct max98090_priv *max98090 = dev_get_drvdata(&client->dev);
snd_soc_unregister_codec(&client->dev);
- regmap_exit(max98090->regmap);
return 0;
}
return 0;
}
-static struct dev_pm_ops max98090_pm = {
+static const struct dev_pm_ops max98090_pm = {
SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
max98090_runtime_resume, NULL)
};
+/*
+ * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
+ *
+ * Copyright (C) 2012 Innovative Converged Devices(ICD)
+ * Copyright (C) 2013 Andrey Smirnov
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.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.
+ *
+ */
+
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/pcm.h>
unsigned int reg)
{
int err;
+ unsigned int val;
struct si476x_core *core = codec->control_data;
si476x_core_lock(core);
- err = si476x_core_cmd_get_property(core, reg);
+ if (!si476x_core_is_powered_up(core))
+ regcache_cache_only(core->regmap, true);
+
+ err = regmap_read(core->regmap, reg, &val);
+
+ if (!si476x_core_is_powered_up(core))
+ regcache_cache_only(core->regmap, false);
si476x_core_unlock(core);
- return err;
+ if (err < 0)
+ return err;
+
+ return val;
}
static int si476x_codec_write(struct snd_soc_codec *codec,
struct si476x_core *core = codec->control_data;
si476x_core_lock(core);
- err = si476x_core_cmd_set_property(core, reg, val);
+ if (!si476x_core_is_powered_up(core))
+ regcache_cache_only(core->regmap, true);
+
+ err = regmap_write(core->regmap, reg, val);
+
+ if (!si476x_core_is_powered_up(core))
+ regcache_cache_only(core->regmap, false);
si476x_core_unlock(core);
return err;
dev_err(codec_dai->codec->dev, "Failed to set output format\n");
return err;
}
-
+
return 0;
}
err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
- (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
+ (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
(width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
if (err < 0) {
dev_err(dai->codec->dev, "Failed to set output width\n");
};
module_platform_driver(si476x_platform_driver);
-MODULE_AUTHOR("Andrey Smirnov <andrey.smirnov@convergeddevices.net>");
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * TAS5086 ASoC codec driver
+ *
+ * Copyright (c) 2013 Daniel Mack <zonque@gmail.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.
+ *
+ * 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.
+ *
+ * TODO:
+ * - implement DAPM and input muxing
+ * - implement modulation limit
+ * - implement non-default PWM start
+ *
+ * Note that this chip has a very unusual register layout, specifically
+ * because the registers are of unequal size, and multi-byte registers
+ * require bulk writes to take effect. Regmap does not support that kind
+ * of devices.
+ *
+ * Currently, the driver does not touch any of the registers >= 0x20, so
+ * it doesn't matter because the entire map can be accessed as 8-bit
+ * array. In case more features will be added in the future
+ * that require access to higher registers, the entire regmap H/W I/O
+ * routines have to be open-coded.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas5086.h>
+
+#define TAS5086_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define TAS5086_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+/*
+ * TAS5086 registers
+ */
+#define TAS5086_CLOCK_CONTROL 0x00 /* Clock control register */
+#define TAS5086_CLOCK_RATE(val) (val << 5)
+#define TAS5086_CLOCK_RATE_MASK (0x7 << 5)
+#define TAS5086_CLOCK_RATIO(val) (val << 2)
+#define TAS5086_CLOCK_RATIO_MASK (0x7 << 2)
+#define TAS5086_CLOCK_SCLK_RATIO_48 (1 << 1)
+#define TAS5086_CLOCK_VALID (1 << 0)
+
+#define TAS5086_DEEMPH_MASK 0x03
+#define TAS5086_SOFT_MUTE_ALL 0x3f
+
+#define TAS5086_DEV_ID 0x01 /* Device ID register */
+#define TAS5086_ERROR_STATUS 0x02 /* Error status register */
+#define TAS5086_SYS_CONTROL_1 0x03 /* System control register 1 */
+#define TAS5086_SERIAL_DATA_IF 0x04 /* Serial data interface register */
+#define TAS5086_SYS_CONTROL_2 0x05 /* System control register 2 */
+#define TAS5086_SOFT_MUTE 0x06 /* Soft mute register */
+#define TAS5086_MASTER_VOL 0x07 /* Master volume */
+#define TAS5086_CHANNEL_VOL(X) (0x08 + (X)) /* Channel 1-6 volume */
+#define TAS5086_VOLUME_CONTROL 0x09 /* Volume control register */
+#define TAS5086_MOD_LIMIT 0x10 /* Modulation limit register */
+#define TAS5086_PWM_START 0x18 /* PWM start register */
+#define TAS5086_SURROUND 0x19 /* Surround register */
+#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */
+#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */
+#define TAS5086_BKNDERR 0x1c
+
+/*
+ * Default TAS5086 power-up configuration
+ */
+static const struct reg_default tas5086_reg_defaults[] = {
+ { 0x00, 0x6c },
+ { 0x01, 0x03 },
+ { 0x02, 0x00 },
+ { 0x03, 0xa0 },
+ { 0x04, 0x05 },
+ { 0x05, 0x60 },
+ { 0x06, 0x00 },
+ { 0x07, 0xff },
+ { 0x08, 0x30 },
+ { 0x09, 0x30 },
+ { 0x0a, 0x30 },
+ { 0x0b, 0x30 },
+ { 0x0c, 0x30 },
+ { 0x0d, 0x30 },
+ { 0x0e, 0xb1 },
+ { 0x0f, 0x00 },
+ { 0x10, 0x02 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+ { 0x18, 0x3f },
+ { 0x19, 0x00 },
+ { 0x1a, 0x18 },
+ { 0x1b, 0x82 },
+ { 0x1c, 0x05 },
+};
+
+static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
+{
+ return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17));
+}
+
+static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_DEV_ID:
+ case TAS5086_ERROR_STATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
+}
+
+struct tas5086_private {
+ struct regmap *regmap;
+ unsigned int mclk, sclk;
+ unsigned int format;
+ bool deemph;
+ /* Current sample rate for de-emphasis control */
+ int rate;
+ /* GPIO driving Reset pin, if any */
+ int gpio_nreset;
+};
+
+static int tas5086_deemph[] = { 0, 32000, 44100, 48000 };
+
+static int tas5086_set_deemph(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int i, val = 0;
+
+ if (priv->deemph)
+ for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++)
+ if (tas5086_deemph[i] == priv->rate)
+ val = i;
+
+ return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1,
+ TAS5086_DEEMPH_MASK, val);
+}
+
+static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = priv->deemph;
+
+ return 0;
+}
+
+static int tas5086_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->deemph = ucontrol->value.enumerated.item[0];
+
+ return tas5086_set_deemph(codec);
+}
+
+
+static int tas5086_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 tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case TAS5086_CLK_IDX_MCLK:
+ priv->mclk = freq;
+ break;
+ case TAS5086_CLK_IDX_SCLK:
+ priv->sclk = freq;
+ break;
+ }
+
+ return 0;
+}
+
+static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* The TAS5086 can only be slave to all clocks */
+ if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ dev_err(codec->dev, "Invalid clocking mode\n");
+ return -EINVAL;
+ }
+
+ /* we need to refer to the data format from hw_params() */
+ priv->format = format;
+
+ return 0;
+}
+
+static const int tas5086_sample_rates[] = {
+ 32000, 38000, 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const int tas5086_ratios[] = {
+ 64, 128, 192, 256, 384, 512
+};
+
+static int index_in_array(const int *array, int len, int needle)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (array[i] == needle)
+ return i;
+
+ return -ENOENT;
+}
+
+static int tas5086_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 tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int val;
+ int ret;
+
+ priv->rate = params_rate(params);
+
+ /* Look up the sample rate and refer to the offset in the list */
+ val = index_in_array(tas5086_sample_rates,
+ ARRAY_SIZE(tas5086_sample_rates), priv->rate);
+
+ if (val < 0) {
+ dev_err(codec->dev, "Invalid sample rate\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_RATE_MASK,
+ TAS5086_CLOCK_RATE(val));
+ if (ret < 0)
+ return ret;
+
+ /* MCLK / Fs ratio */
+ val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
+ priv->mclk / priv->rate);
+ if (val < 0) {
+ dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_RATIO_MASK,
+ TAS5086_CLOCK_RATIO(val));
+ if (ret < 0)
+ return ret;
+
+
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_SCLK_RATIO_48,
+ (priv->sclk == 48 * priv->rate) ?
+ TAS5086_CLOCK_SCLK_RATIO_48 : 0);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The chip has a very unituitive register mapping and muxes information
+ * about data format and sample depth into the same register, but not on
+ * a logical bit-boundary. Hence, we have to refer to the format passed
+ * in the set_dai_fmt() callback and set up everything from here.
+ *
+ * First, determine the 'base' value, using the format ...
+ */
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = 0x00;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = 0x03;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = 0x06;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ /* ... then add the offset for the sample bit depth. */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val += 0;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val += 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ val += 2;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid bit width\n");
+ return -EINVAL;
+ };
+
+ ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val);
+ if (ret < 0)
+ return ret;
+
+ /* clock is considered valid now */
+ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL,
+ TAS5086_CLOCK_VALID, TAS5086_CLOCK_VALID);
+ if (ret < 0)
+ return ret;
+
+ return tas5086_set_deemph(codec);
+}
+
+static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+
+ if (mute)
+ val = TAS5086_SOFT_MUTE_ALL;
+
+ return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val);
+}
+
+/* TAS5086 controls */
+static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new tas5086_controls[] = {
+ SOC_SINGLE_TLV("Master Playback Volume", TAS5086_MASTER_VOL,
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+ TAS5086_CHANNEL_VOL(0), TAS5086_CHANNEL_VOL(1),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+ TAS5086_CHANNEL_VOL(2), TAS5086_CHANNEL_VOL(3),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+ TAS5086_CHANNEL_VOL(4), TAS5086_CHANNEL_VOL(5),
+ 0, 0xff, 1, tas5086_dac_tlv),
+ SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+ tas5086_get_deemph, tas5086_put_deemph),
+};
+
+static const struct snd_soc_dai_ops tas5086_dai_ops = {
+ .hw_params = tas5086_hw_params,
+ .set_sysclk = tas5086_set_dai_sysclk,
+ .set_fmt = tas5086_set_dai_fmt,
+ .mute_stream = tas5086_mute_stream,
+};
+
+static struct snd_soc_dai_driver tas5086_dai = {
+ .name = "tas5086-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 6,
+ .rates = TAS5086_PCM_RATES,
+ .formats = TAS5086_PCM_FORMATS,
+ },
+ .ops = &tas5086_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int tas5086_soc_resume(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ /* Restore codec state */
+ return regcache_sync(priv->regmap);
+}
+#else
+#define tas5086_soc_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+static const struct of_device_id tas5086_dt_ids[] = {
+ { .compatible = "ti,tas5086", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas5086_dt_ids);
+#endif
+
+/* charge period values in microseconds */
+static const int tas5086_charge_period[] = {
+ 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200,
+ 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000,
+ 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000,
+};
+
+static int tas5086_probe(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ int charge_period = 1300000; /* hardware default is 1300 ms */
+ int i, ret;
+
+ if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
+ struct device_node *of_node = codec->dev->of_node;
+ of_property_read_u32(of_node, "ti,charge-period", &charge_period);
+ }
+
+ /* lookup and set split-capacitor charge period */
+ if (charge_period == 0) {
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
+ } else {
+ i = index_in_array(tas5086_charge_period,
+ ARRAY_SIZE(tas5086_charge_period),
+ charge_period);
+ if (i >= 0)
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE,
+ i + 0x08);
+ else
+ dev_warn(codec->dev,
+ "Invalid split-cap charge period of %d ns.\n",
+ charge_period);
+ }
+
+ /* enable factory trim */
+ ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00);
+ if (ret < 0)
+ return ret;
+
+ /* start all channels */
+ ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20);
+ if (ret < 0)
+ return ret;
+
+ /* set master volume to 0 dB */
+ ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30);
+ if (ret < 0)
+ return ret;
+
+ /* mute all channels for now */
+ ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE,
+ TAS5086_SOFT_MUTE_ALL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas5086_remove(struct snd_soc_codec *codec)
+{
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (gpio_is_valid(priv->gpio_nreset))
+ /* Set codec to the reset state */
+ gpio_set_value(priv->gpio_nreset, 0);
+
+ return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
+ .probe = tas5086_probe,
+ .remove = tas5086_remove,
+ .resume = tas5086_soc_resume,
+ .controls = tas5086_controls,
+ .num_controls = ARRAY_SIZE(tas5086_controls),
+};
+
+static const struct i2c_device_id tas5086_i2c_id[] = {
+ { "tas5086", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
+
+static const struct regmap_config tas5086_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(tas5086_reg_defaults),
+ .reg_defaults = tas5086_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5086_volatile_reg,
+ .writeable_reg = tas5086_writeable_reg,
+ .readable_reg = tas5086_accessible_reg,
+};
+
+static int tas5086_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tas5086_private *priv;
+ struct device *dev = &i2c->dev;
+ int gpio_nreset = -EINVAL;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, priv);
+
+ if (of_match_device(of_match_ptr(tas5086_dt_ids), dev)) {
+ struct device_node *of_node = dev->of_node;
+ gpio_nreset = of_get_named_gpio(of_node, "reset-gpio", 0);
+ }
+
+ if (gpio_is_valid(gpio_nreset))
+ if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset"))
+ gpio_nreset = -EINVAL;
+
+ if (gpio_is_valid(gpio_nreset)) {
+ /* Reset codec - minimum assertion time is 400ns */
+ gpio_direction_output(gpio_nreset, 0);
+ udelay(1);
+ gpio_set_value(gpio_nreset, 1);
+
+ /* Codec needs ~15ms to wake up */
+ msleep(15);
+ }
+
+ priv->gpio_nreset = gpio_nreset;
+
+ /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */
+ ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i != 0x3) {
+ dev_err(dev,
+ "Failed to identify TAS5086 codec (got %02x)\n", i);
+ return -ENODEV;
+ }
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086,
+ &tas5086_dai, 1);
+}
+
+static int tas5086_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ return 0;
+}
+
+static struct i2c_driver tas5086_i2c_driver = {
+ .driver = {
+ .name = "tas5086",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tas5086_dt_ids),
+ },
+ .id_table = tas5086_i2c_id,
+ .probe = tas5086_i2c_probe,
+ .remove = tas5086_i2c_remove,
+};
+
+module_i2c_driver(tas5086_i2c_driver);
+
+MODULE_AUTHOR("Daniel Mack <zonque@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments TAS5086 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i]));
}
-static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec)
+static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
{
struct spi_device *spi = to_spi_device(codec->dev);
struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec);
ret = request_firmware(&fw, name, codec->dev);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request application: %d\n",
- ret);
+ dev_err(codec->dev, "Failed to request application(%s): %d\n",
+ name, ret);
return ret;
}
case WM2000_REG_SYS_CTL2:
case WM2000_REG_ANC_STAT:
case WM2000_REG_IF_CTL:
+ case WM2000_REG_ANA_MIC_CTL:
+ case WM2000_REG_SPK_CTL:
return true;
default:
return false;
.reg_bits = 16,
.val_bits = 8,
- .max_register = WM2000_REG_IF_CTL,
+ .max_register = WM2000_REG_SPK_CTL,
.readable_reg = wm2000_readable_reg,
};
#define WM2000_REG_SYS_CTL2 0xf004
#define WM2000_REG_ANC_STAT 0xf005
#define WM2000_REG_IF_CTL 0xf006
+#define WM2000_REG_ANA_MIC_CTL 0xf028
+#define WM2000_REG_SPK_CTL 0xf034
/* SPEECH_CLARITY */
#define WM2000_SPEECH_CLARITY 0x01
return ret;
}
- ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2);
+ ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2);
if (ret != 0)
return ret;
struct wm5102_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
-
- unsigned int spk_ena:2;
- unsigned int spk_ena_pending:1;
};
static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
return 0;
}
+static const char *wm5102_osr_text[] = {
+ "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm5102_osr_val[] = {
+ 0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm5102_hpout_osr[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+ ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+ wm5102_osr_text, wm5102_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
+ ARIZONA_OUT2_OSR_SHIFT, 0x7, 3,
+ wm5102_osr_text, wm5102_osr_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+ ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+ wm5102_osr_text, wm5102_osr_val),
+};
+
#define WM5102_NG_SRC(name, base) \
SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+ ARIZONA_OUT4_OSR_SHIFT, 1, 0),
SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
ARIZONA_OUT5_OSR_SHIFT, 1, 0),
ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
0xbf, 0, digital_tlv),
+SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
+SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
+SOC_VALUE_ENUM("HPOUT3 OSR", wm5102_hpout_osr[2]),
+
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
};
-static int wm5102_spk_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
-
- if (arizona->rev < 1)
- return 0;
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (!wm5102->spk_ena) {
- snd_soc_write(codec, 0x4f5, 0x25a);
- wm5102->spk_ena_pending = true;
- }
- break;
- case SND_SOC_DAPM_POST_PMU:
- if (wm5102->spk_ena_pending) {
- msleep(75);
- snd_soc_write(codec, 0x4f5, 0xda);
- wm5102->spk_ena_pending = false;
- wm5102->spk_ena++;
- }
- break;
- case SND_SOC_DAPM_PRE_PMD:
- wm5102->spk_ena--;
- if (!wm5102->spk_ena)
- snd_soc_write(codec, 0x4f5, 0x25a);
- break;
- case SND_SOC_DAPM_POST_PMD:
- if (!wm5102->spk_ena)
- snd_soc_write(codec, 0x4f5, 0x0da);
- break;
- }
-
- return 0;
-}
-
-
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 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_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
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("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
return arizona_set_fll(&wm5102->fll[0], source, Fref, Fout);
case WM5102_FLL2:
return arizona_set_fll(&wm5102->fll[1], source, Fref, Fout);
+ case WM5102_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm5102->fll[0], source, Fref,
+ Fout);
+ case WM5102_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm5102->fll[1], source, Fref,
+ Fout);
default:
return -EINVAL;
}
if (ret != 0)
return ret;
- ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1);
+ ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2);
if (ret != 0)
return ret;
+ arizona_init_spk(codec);
+
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
priv->core.arizona->dapm = &codec->dapm;
#define WM5102_DIG_VU 0x0200
static unsigned int wm5102_digital_vu[] = {
- ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R,
- ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_ADC_DIGITAL_VOLUME_2R,
- ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_ADC_DIGITAL_VOLUME_3R,
-
ARIZONA_DAC_DIGITAL_VOLUME_1L,
ARIZONA_DAC_DIGITAL_VOLUME_1R,
ARIZONA_DAC_DIGITAL_VOLUME_2L,
platform_set_drvdata(pdev, wm5102);
wm5102->core.arizona = arizona;
+ wm5102->core.num_inputs = 6;
wm5102->core.adsp[0].part = "wm5102";
wm5102->core.adsp[0].num = 1;
ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
&wm5102->fll[1]);
+ /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+ ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+ regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+ ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
for (i = 0; i < ARRAY_SIZE(wm5102_dai); i++)
arizona_init_dai(&wm5102->core, i);
#include "arizona.h"
-#define WM5102_FLL1 1
-#define WM5102_FLL2 2
+#define WM5102_FLL1 1
+#define WM5102_FLL2 2
+#define WM5102_FLL1_REFCLK 3
+#define WM5102_FLL2_REFCLK 4
#endif
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("IN4R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4R_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
-SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+ ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+ ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
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),
-SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
return arizona_set_fll(&wm5110->fll[0], source, Fref, Fout);
case WM5110_FLL2:
return arizona_set_fll(&wm5110->fll[1], source, Fref, Fout);
+ case WM5110_FLL1_REFCLK:
+ return arizona_set_fll_refclk(&wm5110->fll[0], source, Fref,
+ Fout);
+ case WM5110_FLL2_REFCLK:
+ return arizona_set_fll_refclk(&wm5110->fll[1], source, Fref,
+ Fout);
default:
return -EINVAL;
}
#define WM5110_DIG_VU 0x0200
static unsigned int wm5110_digital_vu[] = {
- ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R,
- ARIZONA_ADC_DIGITAL_VOLUME_2L,
- 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_2L,
platform_set_drvdata(pdev, wm5110);
wm5110->core.arizona = arizona;
+ wm5110->core.num_inputs = 8;
for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++)
wm5110->fll[i].vco_mult = 3;
#include "arizona.h"
-#define WM5110_FLL1 1
-#define WM5110_FLL2 2
+#define WM5110_FLL1 1
+#define WM5110_FLL2 2
+#define WM5110_FLL1_REFCLK 3
+#define WM5110_FLL2_REFCLK 4
#endif
/* ALSA can only do steps of .01dB */
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
SOC_ENUM("DAC Companding Mode", dac_companding),
SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
+SOC_SINGLE_TLV("DAC Boost Volume", WM8903_AUDIO_INTERFACE_0, 9, 3, 0,
+ dac_boost_tlv),
SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
wm8903_get_deemph, wm8903_put_deemph),
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1);
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
7, 1, 0),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
+ WM8960_INBMIX1, 4, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume",
+ WM8960_INBMIX1, 1, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume",
+ WM8960_INBMIX2, 4, 7, 0, boost_tlv),
+SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume",
+ WM8960_INBMIX2, 1, 7, 0, boost_tlv),
+
SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
0, 255, 0, dac_tlv),
vmid_reference(codec);
break;
case WM8958:
- if (wm8994->revision < 1)
+ if (control->revision < 1)
vmid_reference(codec);
break;
default:
vmid_dereference(codec);
break;
case WM8958:
- if (wm8994->revision < 1)
+ if (control->revision < 1)
vmid_dereference(codec);
break;
default:
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+
+ wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+ wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+
snd_soc_update_bits(codec, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
snd_soc_update_bits(codec, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
+ } else if (wm8994->aifdiv[0]) {
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK,
+ wm8994->aifdiv[0]);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK,
+ wm8994->aifdiv[1]);
+
+ wm8994->aifdiv[0] = 0;
+ wm8994->aifdiv[1] = 0;
}
return 0;
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
dev_dbg(codec->dev, "Configuring AIFs for 128fs\n");
+
+ wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+ wm8994->aifdiv[1] = snd_soc_read(codec, WM8994_AIF2_RATE)
+ & WM8994_AIF1CLK_RATE_MASK;
+
snd_soc_update_bits(codec, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
snd_soc_update_bits(codec, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
+ } else if (wm8994->aifdiv[0]) {
+ snd_soc_update_bits(codec, WM8994_AIF1_RATE,
+ WM8994_AIF1CLK_RATE_MASK,
+ wm8994->aifdiv[0]);
+ snd_soc_update_bits(codec, WM8994_AIF2_RATE,
+ WM8994_AIF2CLK_RATE_MASK,
+ wm8994->aifdiv[1]);
+
+ wm8994->aifdiv[0] = 0;
+ wm8994->aifdiv[1] = 0;
}
return 0;
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
switch (control->type) {
case WM8958:
- if (wm8994->revision == 0) {
+ if (control->revision == 0) {
/* Optimise performance for rev A */
snd_soc_update_bits(codec,
WM8958_CHARGE_PUMP_2,
{
struct snd_soc_codec *codec = dai->codec;
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->wm8994;
+ struct wm8994_pdata *pdata = &control->pdata;
int aif1_reg;
int aif2_reg;
int bclk_reg;
}
wm8994->channels[id] = params_channels(params);
- switch (params_channels(params)) {
+ if (pdata->max_channels_clocked[id] &&
+ wm8994->channels[id] > pdata->max_channels_clocked[id]) {
+ dev_dbg(dai->dev, "Constraining channels to %d from %d\n",
+ pdata->max_channels_clocked[id], wm8994->channels[id]);
+ wm8994->channels[id] = pdata->max_channels_clocked[id];
+ }
+
+ switch (wm8994->channels[id]) {
case 1:
case 2:
bclk_rate *= 2;
dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
dai->id, wm8994->aifclk[id], bclk_rate);
- if (params_channels(params) == 1 &&
+ if (wm8994->channels[id] == 1 &&
(snd_soc_read(codec, aif1_reg) & 0x18) == 0x18)
aif2 |= WM8994_AIF1_MONO;
int i, ret;
unsigned int val, mask;
- if (wm8994->revision < 4) {
+ if (control->revision < 4) {
/* force a HW read */
ret = regmap_read(control->regmap,
WM8994_POWER_MANAGEMENT_5, &val);
codec->dapm.idle_bias_off = 1;
/* Set revision-specific configuration */
- wm8994->revision = snd_soc_read(codec, WM8994_CHIP_REVISION);
switch (control->type) {
case WM8994:
/* Single ended line outputs should have VMID on. */
!control->pdata.lineout2_diff)
codec->dapm.idle_bias_off = 0;
- switch (wm8994->revision) {
+ switch (control->revision) {
case 2:
case 3:
wm8994->hubs.dcs_codes_l = -5;
wm8994->hubs.dcs_readback_mode = 1;
wm8994->hubs.hp_startup_mode = 1;
- switch (wm8994->revision) {
+ switch (control->revision) {
case 0:
break;
default:
switch (control->type) {
case WM1811:
- if (control->cust_id > 1 || wm8994->revision > 1) {
+ if (control->cust_id > 1 || control->revision > 1) {
ret = wm8994_request_irq(wm8994->wm8994,
WM8994_IRQ_GPIO(6),
wm1811_jackdet_irq, "JACKDET",
case WM8994:
snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets,
ARRAY_SIZE(wm8994_specific_dapm_widgets));
- if (wm8994->revision < 4) {
+ if (control->revision < 4) {
snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
ARRAY_SIZE(wm8994_lateclk_revd_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
ARRAY_SIZE(wm8958_snd_controls));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
- if (wm8994->revision < 1) {
+ if (control->revision < 1) {
snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
ARRAY_SIZE(wm8994_lateclk_revd_widgets));
snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
snd_soc_dapm_add_routes(dapm, wm8994_intercon,
ARRAY_SIZE(wm8994_intercon));
- if (wm8994->revision < 4) {
+ if (control->revision < 4) {
snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
ARRAY_SIZE(wm8994_revd_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
}
break;
case WM8958:
- if (wm8994->revision < 1) {
+ if (control->revision < 1) {
snd_soc_dapm_add_routes(dapm, wm8994_intercon,
ARRAY_SIZE(wm8994_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
int sysclk_rate[2];
int mclk[2];
int aifclk[2];
+ int aifdiv[2];
int channels[2];
struct wm8994_fll_config fll[2], fll_suspend[2];
struct completion fll_locked[2];
wm1811_mic_id_cb mic_id_cb;
void *mic_id_cb_data;
- int revision;
-
unsigned int aif1clk_enable:1;
unsigned int aif2clk_enable:1;
#include <linux/mfd/arizona/registers.h>
+#include "arizona.h"
#include "wm_adsp.h"
#define adsp_crit(_dsp, fmt, ...) \
#define WM_ADSP_NUM_FW 4
+#define WM_ADSP_FW_MBC_VSS 0
+#define WM_ADSP_FW_TX 1
+#define WM_ADSP_FW_TX_SPK 2
+#define WM_ADSP_FW_RX_ANC 3
+
static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
- "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
+ [WM_ADSP_FW_MBC_VSS] = "MBC/VSS",
+ [WM_ADSP_FW_TX] = "Tx",
+ [WM_ADSP_FW_TX_SPK] = "Tx Speaker",
+ [WM_ADSP_FW_RX_ANC] = "Rx ANC",
};
static struct {
const char *file;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
- { .file = "mbc-vss" },
- { .file = "tx" },
- { .file = "tx-spk" },
- { .file = "rx-anc" },
+ [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
+ [WM_ADSP_FW_TX] = { .file = "tx" },
+ [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" },
+ [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
-const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
+const struct snd_kcontrol_new wm_adsp1_fw_controls[] = {
+ SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+};
+EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls);
+
+#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA)
+static const struct soc_enum wm_adsp2_rate_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+ SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1,
+ ARIZONA_DSP1_RATE_SHIFT, 0xf,
+ ARIZONA_RATE_ENUM_SIZE,
+ arizona_rate_text, arizona_rate_val),
+};
+
+const struct snd_kcontrol_new wm_adsp2_fw_controls[] = {
SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]),
SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]),
SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]),
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]),
};
-EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
+EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls);
+#endif
static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
buf_size = sizeof(adsp1_id);
algs = be32_to_cpu(adsp1_id.algs);
+ dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- be32_to_cpu(adsp1_id.fw.id),
+ dsp->fw_id,
(be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp1_id.fw.ver) & 0xff,
algs);
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_ZM;
+ region->alg = be32_to_cpu(adsp1_id.fw.id);
+ region->base = be32_to_cpu(adsp1_id.zm);
+ list_add_tail(®ion->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_DM;
+ region->alg = be32_to_cpu(adsp1_id.fw.id);
+ region->base = be32_to_cpu(adsp1_id.dm);
+ list_add_tail(®ion->list, &dsp->alg_regions);
+
pos = sizeof(adsp1_id) / 2;
term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
break;
buf_size = sizeof(adsp2_id);
algs = be32_to_cpu(adsp2_id.algs);
+ dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- be32_to_cpu(adsp2_id.fw.id),
+ dsp->fw_id,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp2_id.fw.ver) & 0xff,
algs);
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_XM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.xm);
+ list_add_tail(®ion->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_YM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.ym);
+ list_add_tail(®ion->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_ZM;
+ region->alg = be32_to_cpu(adsp2_id.fw.id);
+ region->base = be32_to_cpu(adsp2_id.zm);
+ list_add_tail(®ion->list, &dsp->alg_regions);
+
pos = sizeof(adsp2_id) / 2;
term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
break;
case (WMFW_INFO_TEXT << 8):
break;
case (WMFW_ABSOLUTE << 8):
- region_name = "register";
- reg = offset;
+ /*
+ * Old files may use this for global
+ * coefficients.
+ */
+ if (le32_to_cpu(blk->id) == dsp->fw_id &&
+ offset == 0) {
+ region_name = "global coefficients";
+ mem = wm_adsp_find_region(dsp, type);
+ if (!mem) {
+ adsp_err(dsp, "No ZM\n");
+ break;
+ }
+ reg = wm_adsp_region_to_reg(mem, 0);
+
+ } else {
+ region_name = "register";
+ reg = offset;
+ }
break;
case WMFW_ADSP1_DM:
struct list_head alg_regions;
+ int fw_id;
+
const struct wm_adsp_region *mem;
int num_mems;
.shift = num, .event = wm_adsp2_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
-extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
+extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
+extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
int wm_adsp1_init(struct wm_adsp *adsp);
int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
list_add_tail(&cache->list, &hubs->dcs_cache);
}
-static void wm_hubs_read_dc_servo(struct snd_soc_codec *codec,
+static int 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;
+ int ret = 0;
switch (hubs->dcs_readback_mode) {
case 2:
break;
default:
WARN(1, "Unknown DCS readback method\n");
- return;
+ ret = -1;
}
+ return ret;
}
/*
WM8993_DCS_TRIG_STARTUP_1);
}
- wm_hubs_read_dc_servo(codec, ®_l, ®_r);
+ if (wm_hubs_read_dc_servo(codec, ®_l, ®_r) < 0)
+ return;
dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
};
+static const struct snd_soc_component_driver davinci_i2s_component = {
+ .name = "davinci-i2s",
+};
+
static int davinci_i2s_probe(struct platform_device *pdev)
{
struct snd_platform_data *pdata = pdev->dev.platform_data;
dev_set_drvdata(&pdev->dev, dev);
- ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
+ &davinci_i2s_dai, 1);
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;
+ goto err_unregister_component;
}
return 0;
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&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);
+ snd_soc_unregister_component(&pdev->dev);
davinci_soc_platform_unregister(&pdev->dev);
clk_disable(dev->clk);
#define DISMOD (val)(val<<2)
#define TXSTATE BIT(4)
#define RXSTATE BIT(5)
+#define SRMOD_MASK 3
+#define SRMOD_INACTIVE 0
/*
* DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
/* mapping of the XSSZ bit-field as described in the datasheet */
fmt = (word_length >> 1) - 1;
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
- RXSSZ(fmt), RXSSZ(0x0F));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
- TXSSZ(fmt), TXSSZ(0x0F));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXROT(rotate),
- TXROT(7));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXROT(rotate),
- RXROT(7));
+ if (dev->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
+ RXSSZ(fmt), RXSSZ(0x0F));
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+ TXSSZ(fmt), TXSSZ(0x0F));
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+ TXROT(rotate), TXROT(7));
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
+ RXROT(rotate), RXROT(7));
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG,
+ mask);
+ }
+
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, mask);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, mask);
return 0;
}
-static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream)
+static int davinci_hw_common_param(struct davinci_audio_dev *dev, int stream,
+ int channels)
{
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
-
+ u8 ser;
+ u8 slots = dev->tdm_slots;
+ u8 max_active_serializers = (channels + slots - 1) / slots;
/* Default configuration */
mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
for (i = 0; i < dev->num_serializer; i++) {
mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
dev->serial_dir[i]);
- if (dev->serial_dir[i] == TX_MODE) {
+ if (dev->serial_dir[i] == TX_MODE &&
+ tx_ser < max_active_serializers) {
mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
AXR(i));
tx_ser++;
- } else if (dev->serial_dir[i] == RX_MODE) {
+ } else if (dev->serial_dir[i] == RX_MODE &&
+ rx_ser < max_active_serializers) {
mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
AXR(i));
rx_ser++;
+ } else {
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
+ SRMOD_INACTIVE, SRMOD_MASK);
}
}
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ser = tx_ser;
+ else
+ ser = rx_ser;
+
+ if (ser < max_active_serializers) {
+ dev_warn(dev->dev, "stream has more channels (%d) than are "
+ "enabled in mcasp (%d)\n", channels, ser * slots);
+ return -EINVAL;
+ }
+
if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (dev->txnumevt * tx_ser > 64)
dev->txnumevt = 1;
((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
}
}
+
+ return 0;
}
static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
/* S/PDIF */
static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
{
- /* Set the PDIR for Serialiser as output */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX);
-
- /* TXMASK for 24 bits */
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF);
-
/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
and LSB first */
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
&dev->dma_params[substream->stream];
int word_length;
u8 fifo_level;
+ u8 slots = dev->tdm_slots;
+ int channels;
+ struct snd_interval *pcm_channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ channels = pcm_channels->min;
- davinci_hw_common_param(dev, substream->stream);
+ if (davinci_hw_common_param(dev, substream->stream, channels) == -EINVAL)
+ return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
fifo_level = dev->txnumevt;
else
dma_params->acnt = dma_params->data_type;
dma_params->fifo_level = fifo_level;
+ dma_params->active_serializers = (channels + slots - 1) / slots;
davinci_config_channel_size(dev, word_length);
return 0;
.name = "davinci-mcasp.0",
.playback = {
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 32 * 16,
.rates = DAVINCI_MCASP_RATES,
.formats = DAVINCI_MCASP_PCM_FMTS,
},
.capture = {
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 32 * 16,
.rates = DAVINCI_MCASP_RATES,
.formats = DAVINCI_MCASP_PCM_FMTS,
},
};
+static const struct snd_soc_component_driver davinci_mcasp_component = {
+ .name = "davinci-mcasp",
+};
+
static const struct of_device_id mcasp_dt_ids[] = {
{
.compatible = "ti,dm646x-mcasp-audio",
pdata->op_mode = val;
ret = of_property_read_u32(np, "tdm-slots", &val);
- if (ret >= 0)
+ if (ret >= 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev,
+ "tdm-slots must be in rage [2-32]\n");
+ ret = -EINVAL;
+ goto nodata;
+ }
+
pdata->tdm_slots = val;
+ }
ret = of_property_read_u32(np, "num-serializer", &val);
if (ret >= 0)
dma_data->channel = res->start;
dev_set_drvdata(&pdev->dev, dev);
- ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);
+ ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component,
+ &davinci_mcasp_dai[pdata->op_mode], 1);
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;
+ goto err_unregister_component;
}
return 0;
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
err_release_clk:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
static int davinci_mcasp_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
davinci_soc_platform_unregister(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
unsigned short acnt;
unsigned int count;
unsigned int fifo_level;
+ unsigned char serializers = prtd->params->active_serializers;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
data_type = prtd->params->data_type;
count = period_size / data_type;
if (fifo_level)
- count /= fifo_level;
+ count /= fifo_level * serializers;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
- dst_bidx = 0;
- src_cidx = data_type * fifo_level;
+ dst_bidx = 4;
+ src_cidx = data_type * fifo_level * serializers;
dst_cidx = 0;
} else {
src = prtd->params->dma_addr;
src_bidx = 0;
dst_bidx = data_type;
src_cidx = 0;
- dst_cidx = data_type * fifo_level;
+ dst_cidx = data_type * fifo_level * serializers;
}
acnt = prtd->params->acnt;
edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0,
ASYNC);
else
- edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
- count, fifo_level,
- ABSYNC);
+ edma_set_transfer_params(prtd->asp_link[0], acnt,
+ fifo_level * serializers,
+ count, fifo_level * serializers,
+ ABSYNC);
}
static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
unsigned int fifo_level;
+ unsigned char active_serializers; /* num. of active audio serializers */
};
int davinci_soc_platform_register(struct device *dev);
};
+static const struct snd_soc_component_driver davinci_vcif_component = {
+ .name = "davinci-vcif",
+};
+
static int davinci_vcif_probe(struct platform_device *pdev)
{
struct davinci_vc *davinci_vc = pdev->dev.platform_data;
dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
- ret = snd_soc_register_dai(&pdev->dev, &davinci_vcif_dai);
+ ret = snd_soc_register_component(&pdev->dev, &davinci_vcif_component,
+ &davinci_vcif_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "could not register dai\n");
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);
+ snd_soc_unregister_component(&pdev->dev);
return ret;
}
static int davinci_vcif_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
davinci_soc_platform_unregister(&pdev->dev);
return 0;
.trigger = dw_i2s_trigger,
};
+static const struct snd_soc_component_driver dw_i2s_component = {
+ .name = "dw-i2s",
+};
+
#ifdef CONFIG_PM
static int dw_i2s_suspend(struct snd_soc_dai *dai)
dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev);
- ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
+ dw_i2s_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "not able to register dai\n");
goto err_set_drvdata;
{
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(dev->clk);
.ops = &fsl_ssi_dai_ops,
};
+static const struct snd_soc_component_driver fsl_ssi_component = {
+ .name = "fsl-ssi",
+};
+
/* Show the statistics of a flag only if its interrupt is enabled. The
* compiler will optimze this code to a no-op if the interrupt is not
* enabled.
/* Register with ASoC */
dev_set_drvdata(&pdev->dev, ssi_private);
- ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
+ ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
+ &ssi_private->cpu_dai_drv, 1);
if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
goto error_dev;
error_dai:
if (ssi_private->ssi_on_imx)
platform_device_unregister(ssi_private->imx_pcm_pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
error_dev:
dev_set_drvdata(&pdev->dev, NULL);
clk_disable_unprepare(ssi_private->clk);
clk_put(ssi_private->clk);
}
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
free_irq(ssi_private->irq, ssi_private);
.ops = &imx_ssi_pcm_dai_ops,
};
+static const struct snd_soc_component_driver imx_component = {
+ .name = DRV_NAME,
+};
+
static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
{
void __iomem *base = imx_ssi->base;
platform_set_drvdata(pdev, ssi);
- ret = snd_soc_register_dai(&pdev->dev, dai);
+ ret = snd_soc_register_component(&pdev->dev, &imx_component,
+ dai, 1);
if (ret) {
dev_err(&pdev->dev, "register DAI failed\n");
goto failed_register;
failed_pdev_fiq_add:
platform_device_put(ssi->soc_platform_pdev_fiq);
failed_pdev_fiq_alloc:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
failed_register:
release_mem_region(res->start, resource_size(res));
failed_get_resource:
platform_device_unregister(ssi->soc_platform_pdev);
platform_device_unregister(ssi->soc_platform_pdev_fiq);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (ssi->flags & IMX_SSI_USE_AC97)
ac97_ssi = NULL;
.ops = &psc_ac97_digital_ops,
} };
+static const struct snd_soc_component_driver psc_ac97_component = {
+ .name = DRV_NAME,
+};
/* ---------------------------------------------------------------------
if (rc != 0)
return rc;
- rc = snd_soc_register_dais(&op->dev, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
+ rc = snd_soc_register_component(&op->dev, &psc_ac97_component,
+ psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
if (rc != 0) {
dev_err(&op->dev, "Failed to register DAI\n");
return rc;
static int psc_ac97_of_remove(struct platform_device *op)
{
mpc5200_audio_dma_destroy(op);
- snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_ac97_dai));
+ snd_soc_unregister_component(&op->dev);
return 0;
}
.ops = &psc_i2s_dai_ops,
} };
+static const struct snd_soc_component_driver psc_i2s_component = {
+ .name = "mpc5200-i2s",
+};
+
/* ---------------------------------------------------------------------
* OF platform bus binding code:
* - Probe/remove operations
if (rc != 0)
return rc;
- rc = snd_soc_register_dais(&op->dev, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
+ rc = snd_soc_register_component(&op->dev, &psc_i2s_component,
+ psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
if (rc != 0) {
pr_err("Failed to register DAI\n");
return rc;
static int psc_i2s_of_remove(struct platform_device *op)
{
mpc5200_audio_dma_destroy(op);
- snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_i2s_dai));
+ snd_soc_unregister_component(&op->dev);
return 0;
}
.resume = jz4740_i2s_resume,
};
+static const struct snd_soc_component_driver jz4740_i2s_component = {
+ .name = "jz4740-i2s",
+};
+
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
{
struct jz4740_i2s *i2s;
}
platform_set_drvdata(pdev, i2s);
- ret = snd_soc_register_dai(&pdev->dev, &jz4740_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &jz4740_i2s_component,
+ &jz4740_i2s_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register DAI\n");
{
struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(i2s->clk_i2s);
clk_put(i2s->clk_aic);
.ops = &kirkwood_i2s_dai_ops,
};
+static const struct snd_soc_component_driver kirkwood_i2s_component = {
+ .name = DRV_NAME,
+};
+
static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
{
struct kirkwood_asoc_platform_data *data = pdev->dev.platform_data;
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_128;
}
- err = snd_soc_register_dai(&pdev->dev, soc_dai);
+ err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
+ soc_dai, 1);
if (!err)
return 0;
- dev_err(&pdev->dev, "snd_soc_register_dai failed\n");
+ dev_err(&pdev->dev, "snd_soc_register_component failed\n");
if (!IS_ERR(priv->extclk)) {
clk_disable_unprepare(priv->extclk);
{
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (!IS_ERR(priv->extclk)) {
clk_disable_unprepare(priv->extclk);
/*
* sst_platform.c - Intel MID Platform driver
*
- * Copyright (C) 2010-2012 Intel Corp
+ * Copyright (C) 2010-2013 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
},
};
+static const struct snd_soc_component_driver sst_component = {
+ .name = "sst",
+};
+
/* helper functions */
static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
int state)
return stream->compr_ops->get_codec_caps(codec);
}
+static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->set_metadata(stream->id, metadata);
+}
+
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,
+ .set_metadata = sst_platform_compr_set_metadata,
.trigger = sst_platform_compr_trigger,
.pointer = sst_platform_compr_pointer,
.ack = sst_platform_compr_ack,
return ret;
}
- ret = snd_soc_register_dais(&pdev->dev,
+ ret = snd_soc_register_component(&pdev->dev, &sst_component,
sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
if (ret) {
pr_err("registering cpu dais failed\n");
static int sst_platform_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
+ snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);
pr_debug("sst_platform_remove success\n");
return 0;
int (*close) (unsigned int str_id);
int (*get_caps) (struct snd_compr_caps *caps);
int (*get_codec_caps) (struct snd_compr_codec_caps *codec);
+ int (*set_metadata) (unsigned int str_id,
+ struct snd_compr_metadata *mdata);
};
.ops = &mxs_saif_dai_ops,
};
+static const struct snd_soc_component_driver mxs_saif_component = {
+ .name = "mxs-saif",
+};
+
static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
{
struct mxs_saif *saif = dev_id;
platform_set_drvdata(pdev, saif);
- ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai);
+ ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
+ &mxs_saif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "register DAI failed\n");
return ret;
return 0;
failed_pdev_alloc:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return ret;
}
static int mxs_saif_remove(struct platform_device *pdev)
{
mxs_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.ops = &nuc900_ac97_dai_ops,
};
+static const struct snd_soc_component_driver nuc900_ac97_component = {
+ .name = "nuc900-ac97",
+};
+
static int nuc900_ac97_drvprobe(struct platform_device *pdev)
{
struct nuc900_audio *nuc900_audio;
nuc900_ac97_data = nuc900_audio;
- ret = snd_soc_register_dai(&pdev->dev, &nuc900_ac97_dai);
+ ret = snd_soc_register_component(&pdev->dev, &nuc900_ac97_component,
+ &nuc900_ac97_dai, 1);
if (ret)
goto out3;
static int nuc900_ac97_drvremove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(nuc900_ac97_data->clk);
iounmap(nuc900_ac97_data->mmio);
.ops = &omap_dmic_dai_ops,
};
+static const struct snd_soc_component_driver omap_dmic_component = {
+ .name = "omap-dmic",
+};
+
static int asoc_dmic_probe(struct platform_device *pdev)
{
struct omap_dmic *dmic;
if (IS_ERR(dmic->io_base))
return PTR_ERR(dmic->io_base);
- ret = snd_soc_register_dai(&pdev->dev, &omap_dmic_dai);
+ ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component,
+ &omap_dmic_dai, 1);
if (ret)
goto err_put_clk;
{
struct omap_dmic *dmic = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(dmic->fclk);
return 0;
.ops = &omap_hdmi_dai_ops,
};
+static const struct snd_soc_component_driver omap_hdmi_component = {
+ .name = DRV_NAME,
+};
+
static int omap_hdmi_probe(struct platform_device *pdev)
{
int ret;
}
dev_set_drvdata(&pdev->dev, hdmi_data);
- ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
+ ret = snd_soc_register_component(&pdev->dev, &omap_hdmi_component,
+ &omap_hdmi_dai, 1);
return ret;
}
{
struct hdmi_priv *hdmi_data = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (hdmi_data == NULL) {
dev_err(&pdev->dev, "cannot obtain HDMi data\n");
.ops = &mcbsp_dai_ops,
};
+static const struct snd_soc_component_driver omap_mcbsp_component = {
+ .name = "omap-mcbsp",
+};
+
static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
ret = omap_mcbsp_init(pdev);
if (!ret)
- return snd_soc_register_dai(&pdev->dev, &omap_mcbsp_dai);
+ return snd_soc_register_component(&pdev->dev, &omap_mcbsp_component,
+ &omap_mcbsp_dai, 1);
return ret;
}
{
struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(mcbsp->id);
.ops = &omap_mcpdm_dai_ops,
};
+static const struct snd_soc_component_driver omap_mcpdm_component = {
+ .name = "omap-mcpdm",
+};
+
void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
u8 rx1, u8 rx2)
{
mcpdm->dev = &pdev->dev;
- return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
+ return snd_soc_register_component(&pdev->dev, &omap_mcpdm_component,
+ &omap_mcpdm_dai, 1);
}
static int asoc_mcpdm_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.ops = &mmp_sspa_dai_ops,
};
+static const struct snd_soc_component_driver mmp_sspa_component = {
+ .name = "mmp-sspa",
+};
+
static int asoc_mmp_sspa_probe(struct platform_device *pdev)
{
struct sspa_priv *priv;
priv->dai_fmt = (unsigned int) -1;
platform_set_drvdata(pdev, priv);
- return snd_soc_register_dai(&pdev->dev, &mmp_sspa_dai);
+ return snd_soc_register_component(&pdev->dev, &mmp_sspa_component,
+ &mmp_sspa_dai, 1);
}
static int asoc_mmp_sspa_remove(struct platform_device *pdev)
clk_disable(priv->audio_clk);
clk_put(priv->audio_clk);
clk_put(priv->sysclk);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.ops = &pxa_ssp_dai_ops,
};
+static const struct snd_soc_component_driver pxa_ssp_component = {
+ .name = "pxa-ssp",
+};
+
static int asoc_ssp_probe(struct platform_device *pdev)
{
- return snd_soc_register_dai(&pdev->dev, &pxa_ssp_dai);
+ return snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
+ &pxa_ssp_dai, 1);
}
static int asoc_ssp_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.warm_reset = pxa2xx_ac97_warm_reset,
.reset = pxa2xx_ac97_cold_reset,
};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
.name = "AC97 PCM Stereo out",
},
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
+static const struct snd_soc_component_driver pxa_ac97_component = {
+ .name = "pxa-ac97",
+};
static int pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
* driver to do interesting things with the clocking to get us up
* and running.
*/
- return snd_soc_register_dais(&pdev->dev, pxa_ac97_dai_driver,
- ARRAY_SIZE(pxa_ac97_dai_driver));
+ return snd_soc_register_component(&pdev->dev, &pxa_ac97_component,
+ pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver));
}
static int pxa2xx_ac97_dev_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(pxa_ac97_dai_driver));
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.symmetric_rates = 1,
};
+static const struct snd_soc_component_driver pxa_i2s_component = {
+ .name = "pxa-i2s",
+};
+
static int pxa2xx_i2s_drv_probe(struct platform_device *pdev)
{
- return snd_soc_register_dai(&pdev->dev, &pxa_i2s_dai);
+ return snd_soc_register_component(&pdev->dev, &pxa_i2s_component,
+ &pxa_i2s_dai, 1);
}
static int pxa2xx_i2s_drv_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
.ops = &s6000_i2s_dai_ops,
};
+static const struct snd_soc_component_driver s6000_i2s_component = {
+ .name = "s6000-i2s",
+};
+
static int s6000_i2s_probe(struct platform_device *pdev)
{
struct s6000_i2s_dev *dev;
S6_I2S_INT_UNDERRUN |
S6_I2S_INT_OVERRUN);
- ret = snd_soc_register_dai(&pdev->dev, &s6000_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &s6000_i2s_component,
+ &s6000_i2s_dai, 1);
if (ret)
goto err_release_dev;
struct resource *region;
void __iomem *mmio = dev->scbbase;
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
s6000_i2s_stop_channel(dev, 0);
s6000_i2s_stop_channel(dev, 1);
tristate
config SND_SOC_SAMSUNG_NEO1973_WM8753
- tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)"
- depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02)
+ tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)"
+ depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
select SND_S3C24XX_I2S
select SND_SOC_WM8753
- select SND_SOC_LM4857 if MACH_NEO1973_GTA01
select SND_SOC_DFBMCS320
help
Say Y here to enable audio support for the Openmoko Neo1973
#include <sound/soc.h>
#include <mach/dma.h>
-#include <plat/regs-ac97.h>
+#include "regs-ac97.h"
#include <linux/platform_data/asoc-s3c.h>
#include "dma.h"
},
};
+static const struct snd_soc_component_driver s3c_ac97_component = {
+ .name = "s3c-ac97",
+};
+
static int s3c_ac97_probe(struct platform_device *pdev)
{
struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
goto err4;
}
- ret = snd_soc_register_dais(&pdev->dev, s3c_ac97_dai,
- ARRAY_SIZE(s3c_ac97_dai));
+ ret = snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
+ s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
if (ret)
goto err5;
return 0;
err6:
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c_ac97_dai));
+ snd_soc_unregister_component(&pdev->dev);
err5:
free_irq(irq_res->start, NULL);
err4:
struct resource *mem_res, *irq_res;
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(s3c_ac97_dai));
+ snd_soc_unregister_component(&pdev->dev);
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq_res)
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
};
+static const struct snd_soc_component_driver voice_component = {
+ .name = "goni-voice",
+};
+
static struct snd_soc_ops goni_voice_ops = {
.hw_params = goni_voice_hw_params,
};
return -ENOMEM;
/* register voice DAI here */
- ret = snd_soc_register_dai(&goni_snd_device->dev, &voice_dai);
+ ret = snd_soc_register_component(&goni_snd_device->dev, &voice_component,
+ &voice_dai, 1);
if (ret) {
platform_device_put(goni_snd_device);
return ret;
ret = platform_device_add(goni_snd_device);
if (ret) {
- snd_soc_unregister_dai(&goni_snd_device->dev);
+ snd_soc_unregister_component(&goni_snd_device->dev);
platform_device_put(goni_snd_device);
}
static void __exit goni_exit(void)
{
- snd_soc_unregister_dai(&goni_snd_device->dev);
+ snd_soc_unregister_component(&goni_snd_device->dev);
platform_device_unregister(goni_snd_device);
}
#include <sound/soc.h>
#include <sound/jack.h>
-#include <plat/regs-iis.h>
+#include "regs-iis.h"
#include <asm/mach-types.h>
#include "s3c24xx-i2s.h"
.delay = i2s_delay,
};
+static const struct snd_soc_component_driver samsung_i2s_component = {
+ .name = "samsung-i2s",
+};
+
#define SAMSUNG_I2S_RATES SNDRV_PCM_RATE_8000_96000
#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
dev_err(&pdev->dev, "Unable to get drvdata\n");
return -EFAULT;
}
- snd_soc_register_dai(&sec_dai->pdev->dev,
- &sec_dai->i2s_dai_drv);
+ snd_soc_register_component(&sec_dai->pdev->dev,
+ &samsung_i2s_component,
+ &sec_dai->i2s_dai_drv, 1);
asoc_dma_platform_register(&pdev->dev);
return 0;
}
}
}
- snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
+ snd_soc_register_component(&pri_dai->pdev->dev, &samsung_i2s_component,
+ &pri_dai->i2s_dai_drv, 1);
pm_runtime_enable(&pdev->dev);
i2s->sec_dai = NULL;
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
},
{},
};
-MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids);
+MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids);
#ifdef CONFIG_OF
static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
dma_addr_t lp_tx_addr;
} idma;
+static int idma_irq;
+
static void idma_getpos(dma_addr_t *src)
{
*src = idma.lp_tx_addr +
if (prtd == NULL)
return -ENOMEM;
- ret = request_irq(IRQ_I2S0, iis_irq, 0, "i2s", prtd);
+ ret = request_irq(idma_irq, iis_irq, 0, "i2s", prtd);
if (ret < 0) {
pr_err("fail to claim i2s irq , ret = %d\n", ret);
kfree(prtd);
struct snd_pcm_runtime *runtime = substream->runtime;
struct idma_ctrl *prtd = runtime->private_data;
- free_irq(IRQ_I2S0, prtd);
+ free_irq(idma_irq, prtd);
if (!prtd)
pr_err("idma_close called with prtd == NULL\n");
idma.regs = regs;
idma.lp_tx_addr = addr;
}
+EXPORT_SYMBOL_GPL(idma_reg_addr_init);
static struct snd_soc_platform_driver asoc_idma_platform = {
.ops = &idma_ops,
static int asoc_idma_platform_probe(struct platform_device *pdev)
{
+ idma_irq = platform_get_irq(pdev, 0);
+ if (idma_irq < 0)
+ return idma_irq;
+
return snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
}
#include <sound/soc.h>
#include <asm/mach-types.h>
-#include <plat/regs-iis.h>
-#include <mach/gta02.h>
+#include "regs-iis.h"
#include "../codecs/wm8753.h"
#include "s3c24xx-i2s.h"
},
};
+static const struct snd_soc_component_driver s3c_pcm_component = {
+ .name = "s3c-pcm",
+};
+
static int s3c_pcm_dev_probe(struct platform_device *pdev)
{
struct s3c_pcm_info *pcm;
pm_runtime_enable(&pdev->dev);
- ret = snd_soc_register_dai(&pdev->dev, &s3c_pcm_dai[pdev->id]);
+ ret = snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
+ &s3c_pcm_dai[pdev->id], 1);
if (ret != 0) {
dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
goto err5;
return 0;
err6:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
err5:
clk_disable_unprepare(pcm->pclk);
clk_put(pcm->pclk);
struct resource *mem_res;
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
pm_runtime_disable(&pdev->dev);
#include <sound/soc.h>
#include <sound/jack.h>
-#include <plat/regs-iis.h>
+#include "regs-iis.h"
#include <asm/mach-types.h>
#include "s3c24xx-i2s.h"
#define s3c2412_i2s_resume NULL
#endif
-int s3c_i2sv2_register_dai(struct device *dev, int id,
- struct snd_soc_dai_driver *drv)
+int s3c_i2sv2_register_component(struct device *dev, int id,
+ struct snd_soc_component_driver *cmp_drv,
+ struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_dai_ops *ops = drv->ops;
drv->suspend = s3c2412_i2s_suspend;
drv->resume = s3c2412_i2s_resume;
- return snd_soc_register_dai(dev, drv);
+ return snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
}
-EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);
+EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
MODULE_LICENSE("GPL");
unsigned long base);
/**
- * s3c_i2sv2_register_dai - register dai with soc core
+ * s3c_i2sv2_register_component - register component and dai with soc core
* @dev: DAI device
* @id: DAI ID
* @drv: The driver structure to register
* Fill in any missing fields and then register the given dai with the
* soc core.
*/
-extern int s3c_i2sv2_register_dai(struct device *dev, int id,
- struct snd_soc_dai_driver *drv);
+extern int s3c_i2sv2_register_component(struct device *dev, int id,
+ struct snd_soc_component_driver *cmp_drv,
+ struct snd_soc_dai_driver *dai_drv);
#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
.ops = &s3c2412_i2s_dai_ops,
};
+static const struct snd_soc_component_driver s3c2412_i2s_component = {
+ .name = "s3c2412-i2s",
+};
+
static int s3c2412_iis_dev_probe(struct platform_device *pdev)
{
int ret = 0;
- ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai);
+ ret = s3c_i2sv2_register_component(&pdev->dev, -1,
+ &s3c2412_i2s_component,
+ &s3c2412_i2s_dai);
if (ret) {
pr_err("failed to register the dai\n");
return ret;
return 0;
err:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return ret;
}
static int s3c2412_iis_dev_remove(struct platform_device *pdev)
{
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
#include <sound/pcm_params.h>
#include <mach/dma.h>
-#include <plat/regs-iis.h>
+#include "regs-iis.h"
#include "dma.h"
#include "s3c24xx-i2s.h"
.ops = &s3c24xx_i2s_dai_ops,
};
+static const struct snd_soc_component_driver s3c24xx_i2s_component = {
+ .name = "s3c24xx-i2s",
+};
+
static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
{
int ret = 0;
- ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
+ ret = snd_soc_register_component(&pdev->dev, &s3c24xx_i2s_component,
+ &s3c24xx_i2s_dai, 1);
if (ret) {
pr_err("failed to register the dai\n");
return ret;
return 0;
err:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return ret;
}
static int s3c24xx_iis_dev_remove(struct platform_device *pdev)
{
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
#include <sound/soc.h>
#include <sound/s3c24xx_uda134x.h>
-#include <plat/regs-iis.h>
+#include "regs-iis.h"
#include "s3c24xx-i2s.h"
.resume = spdif_resume,
};
+static const struct snd_soc_component_driver samsung_spdif_component = {
+ .name = "samsung-spdif",
+};
+
static int spdif_probe(struct platform_device *pdev)
{
struct s3c_audio_pdata *spdif_pdata;
dev_set_drvdata(&pdev->dev, spdif);
- ret = snd_soc_register_dai(&pdev->dev, &samsung_spdif_dai);
+ ret = snd_soc_register_component(&pdev->dev, &samsung_spdif_component,
+ &samsung_spdif_dai, 1);
if (ret != 0) {
dev_err(&pdev->dev, "fail to register dai\n");
goto err4;
return 0;
err5:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
err4:
iounmap(spdif->regs);
err3:
struct resource *mem_res;
asoc_dma_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
iounmap(spdif->regs);
struct fsi_master {
void __iomem *base;
- int irq;
struct fsi_priv fsia;
struct fsi_priv fsib;
const struct fsi_core *core;
.pcm_free = fsi_pcm_free,
};
+static const struct snd_soc_component_driver fsi_soc_component = {
+ .name = "fsi",
+};
+
/*
* platform function
*/
}
/* master setting */
- master->irq = irq;
master->core = core;
spin_lock_init(&master->lock);
goto exit_fsib;
}
- ret = snd_soc_register_dais(&pdev->dev, fsi_soc_dai,
- ARRAY_SIZE(fsi_soc_dai));
+ ret = snd_soc_register_component(&pdev->dev, &fsi_soc_component,
+ fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
if (ret < 0) {
- dev_err(&pdev->dev, "cannot snd dai register\n");
+ dev_err(&pdev->dev, "cannot snd component register\n");
goto exit_snd_soc;
}
pm_runtime_disable(&pdev->dev);
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai));
+ snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);
fsi_stream_remove(&master->fsia);
#endif
};
+static const struct snd_soc_component_driver sh4_hac_component = {
+ .name = "sh4-hac",
+};
+
static int hac_soc_platform_probe(struct platform_device *pdev)
{
- return snd_soc_register_dais(&pdev->dev, sh4_hac_dai,
- ARRAY_SIZE(sh4_hac_dai));
+ return snd_soc_register_component(&pdev->dev, &sh4_hac_component,
+ sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
}
static int hac_soc_platform_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sh4_hac_dai));
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
static struct snd_soc_dai_link migor_dai = {
.name = "wm8978",
.stream_name = "WM8978",
- .cpu_dai_name = "siu-i2s-dai",
+ .cpu_dai_name = "siu-pcm-audio",
.codec_dai_name = "wm8978-hifi",
.platform_name = "siu-pcm-audio",
.codec_name = "wm8978.0-001a",
.ops = &siu_dai_ops,
};
+static const struct snd_soc_component_driver siu_i2s_component = {
+ .name = "siu-i2s",
+};
+
static int siu_probe(struct platform_device *pdev)
{
const struct firmware *fw_entry;
dev_set_drvdata(&pdev->dev, info);
/* register using ARRAY version so we can keep dai name */
- ret = snd_soc_register_dais(&pdev->dev, &siu_i2s_dai, 1);
+ ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+ &siu_i2s_dai, 1);
if (ret < 0)
goto edaiinit;
return ret;
esocregp:
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
edaiinit:
iounmap(info->reg);
emapreg:
pm_runtime_disable(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
iounmap(info->reg);
iounmap(info->yram);
#endif
};
+static const struct snd_soc_component_driver sh4_ssi_component = {
+ .name = "sh4-ssi",
+};
+
static int sh4_soc_dai_probe(struct platform_device *pdev)
{
- return snd_soc_register_dais(&pdev->dev, sh4_ssi_dai,
- ARRAY_SIZE(sh4_ssi_dai));
+ return snd_soc_register_component(&pdev->dev, &sh4_ssi_component,
+ sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
}
static int sh4_soc_dai_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sh4_ssi_dai));
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
return ret;
}
+static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ 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->set_metadata)
+ ret = platform->driver->compr_ops->set_metadata(cstream, metadata);
+
+ return ret;
+}
+
+static int sst_compr_get_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ 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_metadata)
+ ret = platform->driver->compr_ops->get_metadata(cstream, metadata);
+
+ return ret;
+}
/* 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,
+ .set_metadata = sst_compr_set_metadata,
+ .get_metadata = sst_compr_get_metadata,
.get_params = soc_compr_get_params,
.trigger = soc_compr_trigger,
.pointer = soc_compr_pointer,
static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
+static LIST_HEAD(component_list);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
*
* @dai: DAI to register
*/
-int snd_soc_register_dai(struct device *dev,
+static int snd_soc_register_dai(struct device *dev,
struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_codec *codec;
return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_register_dai);
/**
* snd_soc_unregister_dai - Unregister a DAI from the ASoC core
*
* @dai: DAI to unregister
*/
-void snd_soc_unregister_dai(struct device *dev)
+static void snd_soc_unregister_dai(struct device *dev)
{
struct snd_soc_dai *dai;
kfree(dai->name);
kfree(dai);
}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
/**
* snd_soc_register_dais - Register multiple DAIs with the ASoC core
* @dai: Array of DAIs to register
* @count: Number of DAIs
*/
-int snd_soc_register_dais(struct device *dev,
+static int snd_soc_register_dais(struct device *dev,
struct snd_soc_dai_driver *dai_drv, size_t count)
{
struct snd_soc_codec *codec;
return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_register_dais);
/**
* snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
* @dai: Array of DAIs to unregister
* @count: Number of DAIs
*/
-void snd_soc_unregister_dais(struct device *dev, size_t count)
+static void snd_soc_unregister_dais(struct device *dev, size_t count)
{
int i;
for (i = 0; i < count; i++)
snd_soc_unregister_dai(dev);
}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
/**
* snd_soc_add_platform - Add a platform to the ASoC core
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+
+/**
+ * snd_soc_register_component - Register a component with the ASoC core
+ *
+ */
+int snd_soc_register_component(struct device *dev,
+ const struct snd_soc_component_driver *cmpnt_drv,
+ struct snd_soc_dai_driver *dai_drv,
+ int num_dai)
+{
+ struct snd_soc_component *cmpnt;
+ int ret;
+
+ dev_dbg(dev, "component register %s\n", dev_name(dev));
+
+ cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL);
+ if (!cmpnt) {
+ dev_err(dev, "ASoC: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ cmpnt->name = fmt_single_name(dev, &cmpnt->id);
+ if (!cmpnt->name) {
+ dev_err(dev, "ASoC: Failed to simplifying name\n");
+ return -ENOMEM;
+ }
+
+ cmpnt->dev = dev;
+ cmpnt->driver = cmpnt_drv;
+ cmpnt->num_dai = num_dai;
+
+ /*
+ * snd_soc_register_dai() uses fmt_single_name(), and
+ * snd_soc_register_dais() uses fmt_multiple_name()
+ * for dai->name which is used for name based matching
+ */
+ if (1 == num_dai)
+ ret = snd_soc_register_dai(dev, dai_drv);
+ else
+ ret = snd_soc_register_dais(dev, dai_drv, num_dai);
+ if (ret < 0) {
+ dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
+ goto error_component_name;
+ }
+
+ mutex_lock(&client_mutex);
+ list_add(&cmpnt->list, &component_list);
+ mutex_unlock(&client_mutex);
+
+ dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name);
+
+ return ret;
+
+error_component_name:
+ kfree(cmpnt->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_component);
+
+/**
+ * snd_soc_unregister_component - Unregister a component from the ASoC core
+ *
+ */
+void snd_soc_unregister_component(struct device *dev)
+{
+ struct snd_soc_component *cmpnt;
+
+ list_for_each_entry(cmpnt, &component_list, list) {
+ if (dev == cmpnt->dev)
+ goto found;
+ }
+ return;
+
+found:
+ snd_soc_unregister_dais(dev, cmpnt->num_dai);
+
+ mutex_lock(&client_mutex);
+ list_del(&cmpnt->list);
+ mutex_unlock(&client_mutex);
+
+ dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
+ kfree(cmpnt->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
+
/* Retrieve a card's name from device tree */
int snd_soc_of_parse_card_name(struct snd_soc_card *card,
const char *propname)
return 0;
}
-/* create new dapm mixer control */
-static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
+/*
+ * Determine if a kcontrol is shared. If it is, look it up. If it isn't,
+ * create it. Either way, add the widget into the control's widget list
+ */
+static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
+ int kci, struct snd_soc_dapm_path *path)
{
struct snd_soc_dapm_context *dapm = w->dapm;
- int i, ret = 0;
- size_t name_len, prefix_len;
- struct snd_soc_dapm_path *path;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
+ size_t prefix_len;
+ int shared;
+ struct snd_kcontrol *kcontrol;
struct snd_soc_dapm_widget_list *wlist;
+ int wlistentries;
size_t wlistsize;
+ bool wname_in_long_name, kcname_in_long_name;
+ size_t name_len;
+ char *long_name;
+ const char *name;
+ int ret;
if (dapm->codec)
prefix = dapm->codec->name_prefix;
else
prefix_len = 0;
- /* add kcontrol */
- for (i = 0; i < w->num_kcontrols; i++) {
+ shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],
+ &kcontrol);
- /* match name */
- list_for_each_entry(path, &w->sources, list_sink) {
+ if (kcontrol) {
+ wlist = kcontrol->private_data;
+ wlistentries = wlist->num_widgets + 1;
+ } else {
+ wlist = NULL;
+ wlistentries = 1;
+ }
- /* mixer/mux paths name must match control name */
- if (path->name != (char *)w->kcontrol_news[i].name)
- continue;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *);
+ wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n",
+ w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = wlistentries;
+ wlist->widgets[wlistentries - 1] = w;
- if (w->kcontrols[i]) {
- path->kcontrol = w->kcontrols[i];
- continue;
+ if (!kcontrol) {
+ if (shared) {
+ wname_in_long_name = false;
+ kcname_in_long_name = true;
+ } else {
+ switch (w->id) {
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ wname_in_long_name = true;
+ kcname_in_long_name = true;
+ break;
+ case snd_soc_dapm_mixer_named_ctl:
+ wname_in_long_name = false;
+ kcname_in_long_name = true;
+ break;
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_virt_mux:
+ case snd_soc_dapm_value_mux:
+ wname_in_long_name = true;
+ kcname_in_long_name = false;
+ break;
+ default:
+ kfree(wlist);
+ return -EINVAL;
}
+ }
+
+ if (wname_in_long_name && kcname_in_long_name) {
+ name_len = strlen(w->name) - prefix_len + 1 +
+ strlen(w->kcontrol_news[kci].name) + 1;
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
- sizeof(struct snd_soc_dapm_widget *),
- wlist = kzalloc(wlistsize, GFP_KERNEL);
- if (wlist == NULL) {
- dev_err(dapm->dev,
- "ASoC: can't allocate widget list for %s\n",
- w->name);
+ long_name = kmalloc(name_len, GFP_KERNEL);
+ if (long_name == NULL) {
+ kfree(wlist);
return -ENOMEM;
}
- wlist->num_widgets = 1;
- wlist->widgets[0] = w;
-
- /* add dapm control with long name.
- * for dapm_mixer this is the concatenation of the
- * mixer and kcontrol name.
- * for dapm_mixer_named_ctl this is simply the
- * kcontrol name.
+
+ /*
+ * The control will get a prefix from the control
+ * creation process but we're also using the same
+ * prefix for widgets so cut the prefix off the
+ * front of the widget name.
*/
- name_len = strlen(w->kcontrol_news[i].name) + 1;
- if (w->id != snd_soc_dapm_mixer_named_ctl)
- name_len += 1 + strlen(w->name);
+ snprintf(long_name, name_len, "%s %s",
+ w->name + prefix_len,
+ w->kcontrol_news[kci].name);
+ long_name[name_len - 1] = '\0';
+
+ name = long_name;
+ } else if (wname_in_long_name) {
+ long_name = NULL;
+ name = w->name + prefix_len;
+ } else {
+ long_name = NULL;
+ name = w->kcontrol_news[kci].name;
+ }
- path->long_name = kmalloc(name_len, GFP_KERNEL);
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
+ prefix);
+ ret = snd_ctl_add(card, kcontrol);
+ if (ret < 0) {
+ dev_err(dapm->dev,
+ "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
+ w->name, name, ret);
+ kfree(wlist);
+ kfree(long_name);
+ return ret;
+ }
- if (path->long_name == NULL) {
- kfree(wlist);
- return -ENOMEM;
- }
+ path->long_name = long_name;
+ }
- switch (w->id) {
- default:
- /* The control will get a prefix from
- * the control creation process but
- * we're also using the same prefix
- * for widgets so cut the prefix off
- * the front of the widget name.
- */
- snprintf((char *)path->long_name, name_len,
- "%s %s", w->name + prefix_len,
- w->kcontrol_news[i].name);
- break;
- case snd_soc_dapm_mixer_named_ctl:
- snprintf((char *)path->long_name, name_len,
- "%s", w->kcontrol_news[i].name);
- break;
- }
+ kcontrol->private_data = wlist;
+ w->kcontrols[kci] = kcontrol;
+ path->kcontrol = kcontrol;
- ((char *)path->long_name)[name_len - 1] = '\0';
+ return 0;
+}
- path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
- wlist, path->long_name,
- prefix);
- ret = snd_ctl_add(card, path->kcontrol);
- if (ret < 0) {
- dev_err(dapm->dev, "ASoC: failed to add widget"
- " %s dapm kcontrol %s: %d\n",
- w->name, path->long_name, ret);
- kfree(wlist);
- kfree(path->long_name);
- path->long_name = NULL;
- return ret;
+/* create new dapm mixer control */
+static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
+{
+ int i, ret;
+ struct snd_soc_dapm_path *path;
+
+ /* add kcontrol */
+ for (i = 0; i < w->num_kcontrols; i++) {
+ /* match name */
+ list_for_each_entry(path, &w->sources, list_sink) {
+ /* mixer/mux paths name must match control name */
+ if (path->name != (char *)w->kcontrol_news[i].name)
+ continue;
+
+ if (w->kcontrols[i]) {
+ path->kcontrol = w->kcontrols[i];
+ continue;
}
- w->kcontrols[i] = path->kcontrol;
+
+ ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
+ if (ret < 0)
+ return ret;
}
}
- return ret;
+
+ return 0;
}
/* create new dapm mux control */
static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_dapm_path *path = NULL;
- struct snd_kcontrol *kcontrol;
- struct snd_card *card = dapm->card->snd_card;
- const char *prefix;
- size_t prefix_len;
+ struct snd_soc_dapm_path *path;
int ret;
- struct snd_soc_dapm_widget_list *wlist;
- int shared, wlistentries;
- size_t wlistsize;
- const char *name;
if (w->num_kcontrols != 1) {
dev_err(dapm->dev,
return -EINVAL;
}
- shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0],
- &kcontrol);
- if (kcontrol) {
- wlist = kcontrol->private_data;
- wlistentries = wlist->num_widgets + 1;
- } else {
- wlist = NULL;
- wlistentries = 1;
- }
- wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
- wlistentries * sizeof(struct snd_soc_dapm_widget *),
- wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
- if (wlist == NULL) {
- dev_err(dapm->dev,
- "ASoC: can't allocate widget list for %s\n", w->name);
- return -ENOMEM;
- }
- wlist->num_widgets = wlistentries;
- wlist->widgets[wlistentries - 1] = w;
-
- if (!kcontrol) {
- if (dapm->codec)
- prefix = dapm->codec->name_prefix;
- else
- prefix = NULL;
-
- if (shared) {
- name = w->kcontrol_news[0].name;
- prefix_len = 0;
- } else {
- name = w->name;
- if (prefix)
- prefix_len = strlen(prefix) + 1;
- else
- prefix_len = 0;
- }
-
- /*
- * The control will get a prefix from the control creation
- * process but we're also using the same prefix for widgets so
- * cut the prefix off the front of the widget name.
- */
- kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
- name + prefix_len, prefix);
- ret = snd_ctl_add(card, kcontrol);
- if (ret < 0) {
- dev_err(dapm->dev, "ASoC: failed to add kcontrol %s: %d\n",
- w->name, ret);
- kfree(wlist);
- return ret;
- }
+ path = list_first_entry(&w->sources, struct snd_soc_dapm_path,
+ list_sink);
+ if (!path) {
+ dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name);
+ return -EINVAL;
}
- kcontrol->private_data = wlist;
-
- w->kcontrols[0] = kcontrol;
+ ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path);
+ if (ret < 0)
+ return ret;
list_for_each_entry(path, &w->sources, list_sink)
- path->kcontrol = kcontrol;
+ path->kcontrol = w->kcontrols[0];
return 0;
}
}
/* reset 'walked' bit for each dapm path */
-static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
+static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
+ struct list_head *sink)
{
struct snd_soc_dapm_path *p;
- list_for_each_entry(p, &dapm->card->paths, list)
- p->walked = 0;
+ list_for_each_entry(p, sink, list_source) {
+ if (p->walked) {
+ p->walked = 0;
+ dapm_clear_walk_output(dapm, &p->sink->sinks);
+ }
+ }
}
+static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
+ struct list_head *source)
+{
+ struct snd_soc_dapm_path *p;
+
+ list_for_each_entry(p, source, list_sink) {
+ if (p->walked) {
+ p->walked = 0;
+ dapm_clear_walk_input(dapm, &p->source->sources);
+ }
+ }
+}
+
+
/* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
dapm_reset(card);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
paths = is_connected_output_ep(dai->playback_widget, list);
- else
+ dapm_clear_walk_output(&card->dapm,
+ &dai->playback_widget->sinks);
+ } else {
paths = is_connected_input_ep(dai->capture_widget, list);
+ dapm_clear_walk_input(&card->dapm,
+ &dai->capture_widget->sources);
+ }
trace_snd_soc_dapm_connected(paths, stream);
- dapm_clear_walk(&card->dapm);
mutex_unlock(&card->dapm_mutex);
return paths;
DAPM_UPDATE_STAT(w, power_checks);
in = is_connected_input_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0 && in != 0;
}
if (w->active) {
in = is_connected_input_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_input(w->dapm, &w->sources);
return in != 0;
} else {
return dapm_generic_check_power(w);
if (w->active) {
out = is_connected_output_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0;
} else {
return dapm_generic_check_power(w);
return 1;
}
- dapm_clear_walk(w->dapm);
-
return 0;
}
return -ENOMEM;
in = is_connected_input_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL);
- dapm_clear_walk(w->dapm);
+ dapm_clear_walk_output(w->dapm, &w->sinks);
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
w->name, w->power ? "On" : "Off",
.ops = &spdif_in_dai_ops,
};
+static const struct snd_soc_component_driver spdif_in_component = {
+ .name = "spdif-in",
+};
+
static irqreturn_t spdif_in_irq(int irq, void *arg)
{
struct spdif_in_dev *host = (struct spdif_in_dev *)arg;
return ret;
}
- ret = snd_soc_register_dai(&pdev->dev, &spdif_in_dai);
+ ret = snd_soc_register_component(&pdev->dev, &spdif_in_component,
+ &spdif_in_dai, 1);
if (ret != 0) {
clk_put(host->clk);
return ret;
{
struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(host->clk);
.ops = &spdif_out_dai_ops,
};
+static const struct snd_soc_component_driver spdif_out_component = {
+ .name = "spdif-out",
+};
+
static int spdif_out_probe(struct platform_device *pdev)
{
struct spdif_out_dev *host;
dev_set_drvdata(&pdev->dev, host);
- ret = snd_soc_register_dai(&pdev->dev, &spdif_out_dai);
+ ret = snd_soc_register_component(&pdev->dev, &spdif_out_component,
+ &spdif_out_dai, 1);
if (ret != 0) {
clk_put(host->clk);
return ret;
{
struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(host->clk);
#include <sound/soc.h>
#include <sound/spear_dma.h>
-struct snd_pcm_hardware spear_pcm_hardware = {
+static struct snd_pcm_hardware spear_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
return 0;
}
-struct snd_soc_platform_driver spear_soc_platform = {
+static struct snd_soc_platform_driver spear_soc_platform = {
.ops = &spear_pcm_ops,
.pcm_new = spear_pcm_new,
.pcm_free = spear_pcm_free,
.ops = &tegra20_ac97_dai_ops,
};
+static const struct snd_soc_component_driver tegra20_ac97_component = {
+ .name = DRV_NAME,
+};
+
static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
ac97->capture_dma_data.maxburst = 4;
ac97->capture_dma_data.slave_id = of_dma[0];
- ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1);
+ ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
+ &tegra20_ac97_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
ret = tegra_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ goto err_unregister_component;
}
ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
tegra_asoc_utils_fini(&ac97->util_data);
err_unregister_pcm:
tegra_pcm_platform_unregister(&pdev->dev);
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
err_clk_put:
clk_put(ac97->clk_ac97);
err:
struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
tegra_asoc_utils_fini(&ac97->util_data);
.symmetric_rates = 1,
};
+static const struct snd_soc_component_driver tegra20_i2s_component = {
+ .name = DRV_NAME,
+};
+
static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
goto err_pm_disable;
}
- ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+ ret = snd_soc_register_component(&pdev->dev, &tegra20_i2s_component,
+ &i2s->dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
ret = tegra_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ goto err_unregister_component;
}
return 0;
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_i2s_runtime_suspend(&pdev->dev);
tegra20_i2s_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(i2s->clk_i2s);
.ops = &tegra20_spdif_dai_ops,
};
+static const struct snd_soc_component_driver tegra20_spdif_component = {
+ .name = DRV_NAME,
+};
+
static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
goto err_pm_disable;
}
- ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
+ ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
+ &tegra20_spdif_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
ret = tegra_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ goto err_unregister_component;
}
return 0;
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra20_spdif_runtime_suspend(&pdev->dev);
tegra20_spdif_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(spdif->clk_spdif_out);
}
EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source);
-static const char * const configlink_clocks[] = {
- "i2s0",
- "i2s1",
- "i2s2",
- "i2s3",
- "i2s4",
- "dam0",
- "dam1",
- "dam2",
- "spdif_in",
+#define CLK_LIST_MASK_TEGRA30 BIT(0)
+#define CLK_LIST_MASK_TEGRA114 BIT(1)
+
+#define CLK_LIST_MASK_TEGRA30_OR_LATER \
+ (CLK_LIST_MASK_TEGRA30 | CLK_LIST_MASK_TEGRA114)
+
+static const struct {
+ const char *clk_name;
+ u32 clk_list_mask;
+} configlink_clocks[] = {
+ { "i2s0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s3", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s4", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "spdif_in", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "amx", CLK_LIST_MASK_TEGRA114 },
+ { "adx", CLK_LIST_MASK_TEGRA114 },
};
#define LAST_REG(name) \
.cache_type = REGCACHE_RBTREE,
};
+static struct tegra30_ahub_soc_data soc_data_tegra30 = {
+ .clk_list_mask = CLK_LIST_MASK_TEGRA30,
+};
+
+static struct tegra30_ahub_soc_data soc_data_tegra114 = {
+ .clk_list_mask = CLK_LIST_MASK_TEGRA114,
+};
+
+static const struct of_device_id tegra30_ahub_of_match[] = {
+ { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 },
+ {},
+};
+
static int tegra30_ahub_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
+ const struct tegra30_ahub_soc_data *soc_data;
struct clk *clk;
int i;
struct resource *res0, *res1, *region;
if (ahub)
return -ENODEV;
+ match = of_match_device(tegra30_ahub_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ soc_data = match->data;
+
/*
* The AHUB hosts a register bus: the "configlink". For this to
* operate correctly, all devices on this bus must be out of reset.
* Ensure that here.
*/
for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
- clk = clk_get(&pdev->dev, configlink_clocks[i]);
+ if (!(configlink_clocks[i].clk_list_mask &
+ soc_data->clk_list_mask))
+ continue;
+ clk = clk_get(&pdev->dev, configlink_clocks[i].clk_name);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Can't get clock %s\n",
- configlink_clocks[i]);
+ configlink_clocks[i].clk_name);
ret = PTR_ERR(clk);
goto err;
}
return 0;
}
-static const struct of_device_id tegra30_ahub_of_match[] = {
- { .compatible = "nvidia,tegra30-ahub", },
- {},
-};
-
static const struct dev_pm_ops tegra30_ahub_pm_ops = {
SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
tegra30_ahub_runtime_resume, NULL)
enum tegra30_ahub_txcif txcif);
extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+struct tegra30_ahub_soc_data {
+ u32 clk_list_mask;
+ /*
+ * FIXME: There are many more differences in HW, such as:
+ * - More APBIF channels.
+ * - Extra separate chunks of register address space to represent
+ * the extra APBIF channels.
+ * - More units connected to the AHUB, so that tegra30_ahub_[rt]xcif
+ * need expansion, coupled with there being more defined bits in
+ * the AHUB routing registers.
+ * However, the driver doesn't support those new features yet, so we
+ * don't represent them here yet.
+ */
+};
+
struct tegra30_ahub {
+ const struct tegra30_ahub_soc_data *soc_data;
struct device *dev;
struct clk *clk_d_audio;
struct clk *clk_apbif;
.symmetric_rates = 1,
};
+static const struct snd_soc_component_driver tegra30_i2s_component = {
+ .name = DRV_NAME,
+};
+
static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
goto err_pm_disable;
}
- ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+ ret = snd_soc_register_component(&pdev->dev, &tegra30_i2s_component,
+ &i2s->dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
ret = tegra_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
- goto err_unregister_dai;
+ goto err_unregister_component;
}
return 0;
-err_unregister_dai:
- snd_soc_unregister_dai(&pdev->dev);
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
tegra30_i2s_runtime_suspend(&pdev->dev);
tegra30_i2s_runtime_suspend(&pdev->dev);
tegra_pcm_platform_unregister(&pdev->dev);
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
clk_put(i2s->clk_i2s);
sizeof(struct tegra_alc5632), GFP_KERNEL);
if (!alc5632) {
dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, alc5632);
- if (!(pdev->dev.of_node)) {
- dev_err(&pdev->dev, "Must be instantiated using device tree\n");
- ret = -EINVAL;
- goto err;
- }
-
alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
if (alc5632->gpio_hp_det == -EPROBE_DEFER)
return -EPROBE_DEFER;
goto err;
}
- tegra_alc5632_dai.cpu_of_node = of_parse_phandle(
- pdev->dev.of_node, "nvidia,i2s-controller", 0);
+ tegra_alc5632_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
if (!tegra_alc5632_dai.cpu_of_node) {
dev_err(&pdev->dev,
- "Property 'nvidia,i2s-controller' missing or invalid\n");
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
ret = -EINVAL;
goto err;
}
case 88200:
if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
new_baseclock = 56448000;
- else
+ else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
new_baseclock = 564480000;
+ else
+ new_baseclock = 282240000;
break;
case 8000:
case 16000:
case 96000:
if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
new_baseclock = 73728000;
- else
+ else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
new_baseclock = 552960000;
+ else
+ new_baseclock = 368640000;
break;
default:
return -EINVAL;
struct device *dev)
{
int ret;
+ bool new_clocks = false;
data->dev = dev;
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
else if (of_machine_is_compatible("nvidia,tegra30"))
data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
- else if (!dev->of_node)
- /* non-DT is always Tegra20 */
- data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
- else
- /* DT boot, but unknown SoC */
+ else if (of_machine_is_compatible("nvidia,tegra114")) {
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
+ new_clocks = true;
+ } else {
+ dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
return -EINVAL;
+ }
- data->clk_pll_a = clk_get_sys(NULL, "pll_a");
+ if (new_clocks)
+ data->clk_pll_a = clk_get(dev, "pll_a");
+ else
+ data->clk_pll_a = clk_get_sys(NULL, "pll_a");
if (IS_ERR(data->clk_pll_a)) {
dev_err(data->dev, "Can't retrieve clk pll_a\n");
ret = PTR_ERR(data->clk_pll_a);
goto err;
}
- data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ if (new_clocks)
+ data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
+ else
+ data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
if (IS_ERR(data->clk_pll_a_out0)) {
dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
ret = PTR_ERR(data->clk_pll_a_out0);
goto err_put_pll_a;
}
- if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ if (new_clocks)
+ data->clk_cdev1 = clk_get(dev, "mclk");
+ else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
else
data->clk_cdev1 = clk_get_sys("extern1", NULL);
enum tegra_asoc_utils_soc {
TEGRA_ASOC_UTILS_SOC_TEGRA20,
TEGRA_ASOC_UTILS_SOC_TEGRA30,
+ TEGRA_ASOC_UTILS_SOC_TEGRA114,
};
struct tegra_asoc_utils_data {
static int tegra_wm8753_driver_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &snd_soc_tegra_wm8753;
struct tegra_wm8753 *machine;
int ret;
GFP_KERNEL);
if (!machine) {
dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
card->dev = &pdev->dev;
if (ret)
goto err;
- tegra_wm8753_dai.codec_of_node = of_parse_phandle(
- pdev->dev.of_node, "nvidia,audio-codec", 0);
+ tegra_wm8753_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
if (!tegra_wm8753_dai.codec_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,audio-codec' missing or invalid\n");
goto err;
}
- tegra_wm8753_dai.cpu_of_node = of_parse_phandle(
- pdev->dev.of_node, "nvidia,i2s-controller", 0);
+ tegra_wm8753_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
if (!tegra_wm8753_dai.cpu_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,i2s-controller' missing or invalid\n");
goto err;
}
- tegra_wm8753_dai.platform_of_node =
- tegra_wm8753_dai.cpu_of_node;
+ tegra_wm8753_dai.platform_of_node = tegra_wm8753_dai.cpu_of_node;
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
if (ret)
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/tegra_wm8903.h>
#include "../codecs/wm8903.h"
#define DRV_NAME "tegra-snd-wm8903"
struct tegra_wm8903 {
- struct tegra_wm8903_platform_data pdata;
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
struct tegra_asoc_utils_data util_data;
};
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = &machine->pdata;
- if (!gpio_is_valid(pdata->gpio_spkr_en))
+ if (!gpio_is_valid(machine->gpio_spkr_en))
return 0;
- gpio_set_value_cansleep(pdata->gpio_spkr_en,
+ gpio_set_value_cansleep(machine->gpio_spkr_en,
SND_SOC_DAPM_EVENT_ON(event));
return 0;
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = &machine->pdata;
- if (!gpio_is_valid(pdata->gpio_hp_mute))
+ if (!gpio_is_valid(machine->gpio_hp_mute))
return 0;
- gpio_set_value_cansleep(pdata->gpio_hp_mute,
+ gpio_set_value_cansleep(machine->gpio_hp_mute,
!SND_SOC_DAPM_EVENT_ON(event));
return 0;
SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
-static const struct snd_soc_dapm_route harmony_audio_map[] = {
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Int Spk", NULL, "ROP"},
- {"Int Spk", NULL, "RON"},
- {"Int Spk", NULL, "LOP"},
- {"Int Spk", NULL, "LON"},
- {"Mic Jack", NULL, "MICBIAS"},
- {"IN1L", NULL, "Mic Jack"},
-};
-
static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
SOC_DAPM_PIN_SWITCH("Int Spk"),
};
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_card *card = codec->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
- struct tegra_wm8903_platform_data *pdata = &machine->pdata;
- if (gpio_is_valid(pdata->gpio_hp_det)) {
- tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
+ if (gpio_is_valid(machine->gpio_hp_det)) {
+ tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det;
snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
&tegra_wm8903_hp_jack);
snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
static struct snd_soc_dai_link tegra_wm8903_dai = {
.name = "WM8903",
.stream_name = "WM8903 PCM",
- .codec_name = "wm8903.0-001a",
- .platform_name = "tegra20-i2s.0",
- .cpu_dai_name = "tegra20-i2s.0",
.codec_dai_name = "wm8903-hifi",
.init = tegra_wm8903_init,
.ops = &tegra_wm8903_ops,
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &snd_soc_tegra_wm8903;
struct tegra_wm8903 *machine;
- struct tegra_wm8903_platform_data *pdata;
int ret;
- if (!pdev->dev.platform_data && !pdev->dev.of_node) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
-
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
GFP_KERNEL);
if (!machine) {
dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
- ret = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
- pdata = &machine->pdata;
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
- if (pdev->dev.platform_data) {
- memcpy(pdata, card->dev->platform_data, sizeof(*pdata));
- } else if (np) {
- pdata->gpio_spkr_en = of_get_named_gpio(np,
- "nvidia,spkr-en-gpios", 0);
- if (pdata->gpio_spkr_en == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- pdata->gpio_hp_mute = of_get_named_gpio(np,
- "nvidia,hp-mute-gpios", 0);
- if (pdata->gpio_hp_mute == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- pdata->gpio_hp_det = of_get_named_gpio(np,
- "nvidia,hp-det-gpios", 0);
- if (pdata->gpio_hp_det == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- pdata->gpio_int_mic_en = of_get_named_gpio(np,
- "nvidia,int-mic-en-gpios", 0);
- if (pdata->gpio_int_mic_en == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- pdata->gpio_ext_mic_en = of_get_named_gpio(np,
- "nvidia,ext-mic-en-gpios", 0);
- if (pdata->gpio_ext_mic_en == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- }
-
- if (np) {
- ret = snd_soc_of_parse_card_name(card, "nvidia,model");
- if (ret)
- goto err;
-
- ret = snd_soc_of_parse_audio_routing(card,
- "nvidia,audio-routing");
- if (ret)
- goto err;
-
- tegra_wm8903_dai.codec_name = NULL;
- tegra_wm8903_dai.codec_of_node = of_parse_phandle(np,
- "nvidia,audio-codec", 0);
- if (!tegra_wm8903_dai.codec_of_node) {
- dev_err(&pdev->dev,
- "Property 'nvidia,audio-codec' missing or invalid\n");
- ret = -EINVAL;
- goto err;
- }
-
- tegra_wm8903_dai.cpu_dai_name = NULL;
- tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np,
- "nvidia,i2s-controller", 0);
- if (!tegra_wm8903_dai.cpu_of_node) {
- dev_err(&pdev->dev,
- "Property 'nvidia,i2s-controller' missing or invalid\n");
- ret = -EINVAL;
- goto err;
- }
-
- tegra_wm8903_dai.platform_name = NULL;
- tegra_wm8903_dai.platform_of_node =
- tegra_wm8903_dai.cpu_of_node;
- } else {
- card->dapm_routes = harmony_audio_map;
- card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
- }
-
- if (gpio_is_valid(pdata->gpio_spkr_en)) {
- ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_spkr_en,
+ machine->gpio_spkr_en = of_get_named_gpio(np, "nvidia,spkr-en-gpios",
+ 0);
+ if (machine->gpio_spkr_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_spkr_en)) {
+ ret = devm_gpio_request_one(&pdev->dev, machine->gpio_spkr_en,
GPIOF_OUT_INIT_LOW, "spkr_en");
if (ret) {
dev_err(card->dev, "cannot get spkr_en gpio\n");
}
}
- if (gpio_is_valid(pdata->gpio_hp_mute)) {
- ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_hp_mute,
+ machine->gpio_hp_mute = of_get_named_gpio(np, "nvidia,hp-mute-gpios",
+ 0);
+ if (machine->gpio_hp_mute == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_hp_mute)) {
+ ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_mute,
GPIOF_OUT_INIT_HIGH, "hp_mute");
if (ret) {
dev_err(card->dev, "cannot get hp_mute gpio\n");
}
}
- if (gpio_is_valid(pdata->gpio_int_mic_en)) {
+ machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+ if (machine->gpio_hp_det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ machine->gpio_int_mic_en = of_get_named_gpio(np,
+ "nvidia,int-mic-en-gpios", 0);
+ if (machine->gpio_int_mic_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_int_mic_en)) {
/* Disable int mic; enable signal is active-high */
- ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_int_mic_en,
+ ret = devm_gpio_request_one(&pdev->dev,
+ machine->gpio_int_mic_en,
GPIOF_OUT_INIT_LOW, "int_mic_en");
if (ret) {
dev_err(card->dev, "cannot get int_mic_en gpio\n");
}
}
- if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
+ machine->gpio_ext_mic_en = of_get_named_gpio(np,
+ "nvidia,ext-mic-en-gpios", 0);
+ if (machine->gpio_ext_mic_en == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(machine->gpio_ext_mic_en)) {
/* Enable ext mic; enable signal is active-low */
- ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_ext_mic_en,
+ ret = devm_gpio_request_one(&pdev->dev,
+ machine->gpio_ext_mic_en,
GPIOF_OUT_INIT_LOW, "ext_mic_en");
if (ret) {
dev_err(card->dev, "cannot get ext_mic_en gpio\n");
}
}
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_wm8903_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!tegra_wm8903_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!tegra_wm8903_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_wm8903_dai.platform_of_node = tegra_wm8903_dai.cpu_of_node;
+
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
if (ret)
goto err;
static struct snd_soc_dai_link tegra_wm9712_dai = {
.name = "AC97 HiFi",
.stream_name = "AC97 HiFi",
- .cpu_dai_name = "tegra-ac97-pcm",
+ .cpu_dai_name = "tegra20-ac97",
.codec_dai_name = "wm9712-hifi",
.codec_name = "wm9712-codec",
.init = tegra_wm9712_init,
struct tegra_wm9712 *machine;
int ret;
- if (!pdev->dev.of_node) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
-
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
GFP_KERNEL);
if (!machine) {
static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
.name = "TLV320AIC23",
.stream_name = "AIC23",
- .codec_name = "tlv320aic23-codec.2-001a",
- .platform_name = "tegra20-i2s.0",
- .cpu_dai_name = "tegra20-i2s.0",
.codec_dai_name = "tlv320aic23-hifi",
.ops = &trimslice_asoc_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S |
static int tegra_snd_trimslice_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &snd_soc_trimslice;
struct tegra_trimslice *trimslice;
int ret;
GFP_KERNEL);
if (!trimslice) {
dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
- ret = -ENOMEM;
+ return -ENOMEM;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, trimslice);
+
+ trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!trimslice_tlv320aic23_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
goto err;
}
- if (pdev->dev.of_node) {
- trimslice_tlv320aic23_dai.codec_name = NULL;
- trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle(
- pdev->dev.of_node, "nvidia,audio-codec", 0);
- if (!trimslice_tlv320aic23_dai.codec_of_node) {
- dev_err(&pdev->dev,
- "Property 'nvidia,audio-codec' missing or invalid\n");
- ret = -EINVAL;
- goto err;
- }
-
- trimslice_tlv320aic23_dai.cpu_dai_name = NULL;
- trimslice_tlv320aic23_dai.cpu_of_node = of_parse_phandle(
- pdev->dev.of_node, "nvidia,i2s-controller", 0);
- if (!trimslice_tlv320aic23_dai.cpu_of_node) {
- dev_err(&pdev->dev,
- "Property 'nvidia,i2s-controller' missing or invalid\n");
- ret = -EINVAL;
- goto err;
- }
-
- trimslice_tlv320aic23_dai.platform_name = NULL;
- trimslice_tlv320aic23_dai.platform_of_node =
- trimslice_tlv320aic23_dai.cpu_of_node;
+ trimslice_tlv320aic23_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!trimslice_tlv320aic23_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
}
+ trimslice_tlv320aic23_dai.platform_of_node =
+ trimslice_tlv320aic23_dai.cpu_of_node;
+
ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
if (ret)
goto err;
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- snd_soc_card_set_drvdata(card, trimslice);
-
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
},
};
+static const struct snd_soc_component_driver txx9aclc_ac97_component = {
+ .name = "txx9aclc-ac97",
+};
+
static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
{
struct txx9aclc_plat_drvdata *drvdata;
if (err < 0)
return err;
- return snd_soc_register_dai(&pdev->dev, &txx9aclc_ac97_dai);
+ return snd_soc_register_component(&pdev->dev, &txx9aclc_ac97_component,
+ &txx9aclc_ac97_dai, 1);
}
static int txx9aclc_ac97_dev_remove(struct platform_device *pdev)
{
- snd_soc_unregister_dai(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
return 0;
}
},
};
+static const struct snd_soc_component_driver ux500_msp_component = {
+ .name = "ux500-msp",
+};
+
+
static int ux500_msp_drv_probe(struct platform_device *pdev)
{
struct ux500_msp_i2s_drvdata *drvdata;
}
dev_set_drvdata(&pdev->dev, drvdata);
- ret = snd_soc_register_dai(&pdev->dev,
- &ux500_msp_dai_drv[drvdata->msp->id]);
+ ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component,
+ &ux500_msp_dai_drv[drvdata->msp->id], 1);
if (ret < 0) {
dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
__func__, drvdata->msp->id);
return 0;
err_reg_plat:
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+ snd_soc_unregister_component(&pdev->dev);
err_init_msp:
clk_put(drvdata->clk);
err_clk:
ux500_pcm_unregister_platform(pdev);
- snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+ snd_soc_unregister_component(&pdev->dev);
devm_regulator_put(drvdata->reg_vape);
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
#define FRAME_PER_8_SLOTS 138
#define FRAME_PER_16_SLOTS 277
-#ifndef CONFIG_SND_SOC_UX500_AB5500
#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000
#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
-#else
-#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000
-#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2)
-#endif
#define UX500_MSP_MIN_CHANNELS 1
#define UX500_MSP_MAX_CHANNELS 8
* @dev: device pointer
*
* Allocate a special sound device by minor number from the sound
- * subsystem. The allocated number is returned on success. On failure
+ * subsystem.
+ *
+ * Return: The allocated number is returned on success. On failure,
* a negative error code is returned.
*/
* @dev: Unit number to allocate
*
* Allocate a mixer device. Unit is the number of the mixer requested.
- * Pass -1 to request the next free mixer unit. On success the allocated
- * number is returned, on failure a negative error code is returned.
+ * Pass -1 to request the next free mixer unit.
+ *
+ * Return: On success, the allocated number is returned. On failure,
+ * a negative error code is returned.
*/
int register_sound_mixer(const struct file_operations *fops, int dev)
* @dev: Unit number to allocate
*
* Allocate a midi device. Unit is the number of the midi device requested.
- * Pass -1 to request the next free midi unit. On success the allocated
- * number is returned, on failure a negative error code is returned.
+ * Pass -1 to request the next free midi unit.
+ *
+ * Return: On success, the allocated number is returned. On failure,
+ * a negative error code is returned.
*/
int register_sound_midi(const struct file_operations *fops, int dev)
* @dev: Unit number to allocate
*
* Allocate a DSP device. Unit is the number of the DSP requested.
- * Pass -1 to request the next free DSP unit. On success the allocated
- * number is returned, on failure a negative error code is returned.
+ * Pass -1 to request the next free DSP unit.
*
* This function allocates both the audio and dsp device entries together
* and will always allocate them as a matching pair - eg dsp3/audio3
+ *
+ * Return: On success, the allocated number is returned. On failure,
+ * a negative error code is returned.
*/
int register_sound_dsp(const struct file_operations *fops, int dev)
return 0;
}
-#ifdef CONFIG_PM
-static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg)
+#ifdef CONFIG_PM_SLEEP
+
+static int snd_at73c213_suspend(struct device *dev)
{
- struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_at73c213 *chip = card->private_data;
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
return 0;
}
-static int snd_at73c213_resume(struct spi_device *spi)
+static int snd_at73c213_resume(struct device *dev)
{
- struct snd_card *card = dev_get_drvdata(&spi->dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_at73c213 *chip = card->private_data;
clk_enable(chip->board->dac_clk);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(at73c213_pm_ops, snd_at73c213_suspend,
+ snd_at73c213_resume);
+#define AT73C213_PM_OPS (&at73c213_pm_ops)
+
#else
-#define snd_at73c213_suspend NULL
-#define snd_at73c213_resume NULL
+#define AT73C213_PM_OPS NULL
#endif
static struct spi_driver at73c213_driver = {
.driver = {
.name = "at73c213",
+ .pm = AT73C213_PM_OPS,
},
.probe = snd_at73c213_probe,
- .suspend = snd_at73c213_suspend,
- .resume = snd_at73c213_resume,
.remove = snd_at73c213_remove,
};
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
#define ENDPOINT_CAPTURE 2
#define ENDPOINT_PLAYBACK 6
-#define MAKE_CHECKBYTE(dev,stream,i) \
- (stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
+#define MAKE_CHECKBYTE(cdev,stream,i) \
+ (stream << 1) | (~(i / (cdev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
};
static void
-activate_substream(struct snd_usb_caiaqdev *dev,
+activate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
- spin_lock(&dev->spinlock);
+ spin_lock(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dev->sub_playback[sub->number] = sub;
+ cdev->sub_playback[sub->number] = sub;
else
- dev->sub_capture[sub->number] = sub;
+ cdev->sub_capture[sub->number] = sub;
- spin_unlock(&dev->spinlock);
+ spin_unlock(&cdev->spinlock);
}
static void
-deactivate_substream(struct snd_usb_caiaqdev *dev,
+deactivate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
unsigned long flags;
- spin_lock_irqsave(&dev->spinlock, flags);
+ spin_lock_irqsave(&cdev->spinlock, flags);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dev->sub_playback[sub->number] = NULL;
+ cdev->sub_playback[sub->number] = NULL;
else
- dev->sub_capture[sub->number] = NULL;
+ cdev->sub_capture[sub->number] = NULL;
- spin_unlock_irqrestore(&dev->spinlock, flags);
+ spin_unlock_irqrestore(&cdev->spinlock, flags);
}
static int
return 1;
}
-static int stream_start(struct snd_usb_caiaqdev *dev)
+static int stream_start(struct snd_usb_caiaqdev *cdev)
{
int i, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, dev);
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
- if (dev->streaming)
+ if (cdev->streaming)
return -EINVAL;
- memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
- memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
- dev->input_panic = 0;
- dev->output_panic = 0;
- dev->first_packet = 4;
- dev->streaming = 1;
- dev->warned = 0;
+ memset(cdev->sub_playback, 0, sizeof(cdev->sub_playback));
+ memset(cdev->sub_capture, 0, sizeof(cdev->sub_capture));
+ cdev->input_panic = 0;
+ cdev->output_panic = 0;
+ cdev->first_packet = 4;
+ cdev->streaming = 1;
+ cdev->warned = 0;
for (i = 0; i < N_URBS; i++) {
- ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC);
+ ret = usb_submit_urb(cdev->data_urbs_in[i], GFP_ATOMIC);
if (ret) {
- log("unable to trigger read #%d! (ret %d)\n", i, ret);
- dev->streaming = 0;
+ dev_err(dev, "unable to trigger read #%d! (ret %d)\n",
+ i, ret);
+ cdev->streaming = 0;
return -EPIPE;
}
}
return 0;
}
-static void stream_stop(struct snd_usb_caiaqdev *dev)
+static void stream_stop(struct snd_usb_caiaqdev *cdev)
{
int i;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, dev);
- if (!dev->streaming)
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
+ if (!cdev->streaming)
return;
- dev->streaming = 0;
+ cdev->streaming = 0;
for (i = 0; i < N_URBS; i++) {
- usb_kill_urb(dev->data_urbs_in[i]);
+ usb_kill_urb(cdev->data_urbs_in[i]);
- if (test_bit(i, &dev->outurb_active_mask))
- usb_kill_urb(dev->data_urbs_out[i]);
+ if (test_bit(i, &cdev->outurb_active_mask))
+ usb_kill_urb(cdev->data_urbs_out[i]);
}
- dev->outurb_active_mask = 0;
+ cdev->outurb_active_mask = 0;
}
static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
- debug("%s(%p)\n", __func__, substream);
- substream->runtime->hw = dev->pcm_info;
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
+ substream->runtime->hw = cdev->pcm_info;
snd_pcm_limit_hw_rates(substream->runtime);
+
return 0;
}
static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, substream);
- if (all_substreams_zero(dev->sub_playback) &&
- all_substreams_zero(dev->sub_capture)) {
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
+ if (all_substreams_zero(cdev->sub_playback) &&
+ all_substreams_zero(cdev->sub_capture)) {
/* when the last client has stopped streaming,
* all sample rates are allowed again */
- stream_stop(dev);
- dev->pcm_info.rates = dev->samplerates;
+ stream_stop(cdev);
+ cdev->pcm_info.rates = cdev->samplerates;
}
return 0;
static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub,
struct snd_pcm_hw_params *hw_params)
{
- debug("%s(%p)\n", __func__, sub);
return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
}
static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
- debug("%s(%p)\n", __func__, sub);
- deactivate_substream(dev, sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
+ deactivate_substream(cdev, sub);
return snd_pcm_lib_free_pages(sub);
}
{
int bytes_per_sample, bpp, ret, i;
int index = substream->number;
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p)\n", __func__, substream);
+ dev_dbg(dev, "%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
int out_pos;
- switch (dev->spec.data_alignment) {
+ switch (cdev->spec.data_alignment) {
case 0:
case 2:
out_pos = BYTES_PER_SAMPLE + 1;
break;
}
- dev->period_out_count[index] = out_pos;
- dev->audio_out_buf_pos[index] = out_pos;
+ cdev->period_out_count[index] = out_pos;
+ cdev->audio_out_buf_pos[index] = out_pos;
} else {
int in_pos;
- switch (dev->spec.data_alignment) {
+ switch (cdev->spec.data_alignment) {
case 0:
in_pos = BYTES_PER_SAMPLE + 2;
break;
break;
}
- dev->period_in_count[index] = in_pos;
- dev->audio_in_buf_pos[index] = in_pos;
+ cdev->period_in_count[index] = in_pos;
+ cdev->audio_in_buf_pos[index] = in_pos;
}
- if (dev->streaming)
+ if (cdev->streaming)
return 0;
/* the first client that opens a stream defines the sample rate
* setting for all subsequent calls, until the last client closed. */
for (i=0; i < ARRAY_SIZE(rates); i++)
if (runtime->rate == rates[i])
- dev->pcm_info.rates = 1 << i;
+ cdev->pcm_info.rates = 1 << i;
snd_pcm_limit_hw_rates(runtime);
bytes_per_sample = BYTES_PER_SAMPLE;
- if (dev->spec.data_alignment >= 2)
+ if (cdev->spec.data_alignment >= 2)
bytes_per_sample++;
bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
- * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;
+ * bytes_per_sample * CHANNELS_PER_STREAM * cdev->n_streams;
if (bpp > MAX_ENDPOINT_SIZE)
bpp = MAX_ENDPOINT_SIZE;
- ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate,
+ ret = snd_usb_caiaq_set_audio_params(cdev, runtime->rate,
runtime->sample_bits, bpp);
if (ret)
return ret;
- ret = stream_start(dev);
+ ret = stream_start(cdev);
if (ret)
return ret;
- dev->output_running = 0;
- wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ);
- if (!dev->output_running) {
- stream_stop(dev);
+ cdev->output_running = 0;
+ wait_event_timeout(cdev->prepare_wait_queue, cdev->output_running, HZ);
+ if (!cdev->output_running) {
+ stream_stop(cdev);
return -EPIPE;
}
static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd)
{
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
+ struct device *dev = caiaqdev_to_dev(cdev);
- debug("%s(%p) cmd %d\n", __func__, sub, cmd);
+ dev_dbg(dev, "%s(%p) cmd %d\n", __func__, sub, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- activate_substream(dev, sub);
+ activate_substream(cdev, sub);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- deactivate_substream(dev, sub);
+ deactivate_substream(cdev, sub);
break;
default:
return -EINVAL;
snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub)
{
int index = sub->number;
- struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
+ struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
snd_pcm_uframes_t ptr;
- spin_lock(&dev->spinlock);
+ spin_lock(&cdev->spinlock);
- if (dev->input_panic || dev->output_panic) {
+ if (cdev->input_panic || cdev->output_panic) {
ptr = SNDRV_PCM_POS_XRUN;
goto unlock;
}
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
ptr = bytes_to_frames(sub->runtime,
- dev->audio_out_buf_pos[index]);
+ cdev->audio_out_buf_pos[index]);
else
ptr = bytes_to_frames(sub->runtime,
- dev->audio_in_buf_pos[index]);
+ cdev->audio_in_buf_pos[index]);
unlock:
- spin_unlock(&dev->spinlock);
+ spin_unlock(&cdev->spinlock);
return ptr;
}
.pointer = snd_usb_caiaq_pcm_pointer
};
-static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,
+static void check_for_elapsed_periods(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream **subs)
{
int stream, pb, *cnt;
struct snd_pcm_substream *sub;
- for (stream = 0; stream < dev->n_streams; stream++) {
+ for (stream = 0; stream < cdev->n_streams; stream++) {
sub = subs[stream];
if (!sub)
continue;
pb = snd_pcm_lib_period_bytes(sub);
cnt = (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- &dev->period_out_count[stream] :
- &dev->period_in_count[stream];
+ &cdev->period_out_count[stream] :
+ &cdev->period_in_count[stream];
if (*cnt >= pb) {
snd_pcm_period_elapsed(sub);
}
}
-static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev,
+static void read_in_urb_mode0(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
struct snd_pcm_substream *sub;
int stream, i;
- if (all_substreams_zero(dev->sub_capture))
+ if (all_substreams_zero(cdev->sub_capture))
return;
for (i = 0; i < iso->actual_length;) {
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_capture[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_capture[stream];
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
- audio_buf[dev->audio_in_buf_pos[stream]++]
+ audio_buf[cdev->audio_in_buf_pos[stream]++]
= usb_buf[i];
- dev->period_in_count[stream]++;
- if (dev->audio_in_buf_pos[stream] == sz)
- dev->audio_in_buf_pos[stream] = 0;
+ cdev->period_in_count[stream]++;
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
}
}
}
}
-static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
+static void read_in_urb_mode2(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
int stream, i;
for (i = 0; i < iso->actual_length;) {
- if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
+ if (i % (cdev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
for (stream = 0;
- stream < dev->n_streams;
+ stream < cdev->n_streams;
stream++, i++) {
- if (dev->first_packet)
+ if (cdev->first_packet)
continue;
- check_byte = MAKE_CHECKBYTE(dev, stream, i);
+ check_byte = MAKE_CHECKBYTE(cdev, stream, i);
if ((usb_buf[i] & 0x3f) != check_byte)
- dev->input_panic = 1;
+ cdev->input_panic = 1;
if (usb_buf[i] & 0x80)
- dev->output_panic = 1;
+ cdev->output_panic = 1;
}
}
- dev->first_packet = 0;
+ cdev->first_packet = 0;
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_capture[stream];
- if (dev->input_panic)
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_capture[stream];
+ if (cdev->input_panic)
usb_buf[i] = 0;
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
- audio_buf[dev->audio_in_buf_pos[stream]++] =
+ audio_buf[cdev->audio_in_buf_pos[stream]++] =
usb_buf[i];
- dev->period_in_count[stream]++;
- if (dev->audio_in_buf_pos[stream] == sz)
- dev->audio_in_buf_pos[stream] = 0;
+ cdev->period_in_count[stream]++;
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
}
}
}
}
-static void read_in_urb_mode3(struct snd_usb_caiaqdev *dev,
+static void read_in_urb_mode3(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
+ struct device *dev = caiaqdev_to_dev(cdev);
int stream, i;
/* paranoia check */
return;
for (i = 0; i < iso->actual_length;) {
- for (stream = 0; stream < dev->n_streams; stream++) {
- struct snd_pcm_substream *sub = dev->sub_capture[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = cdev->sub_capture[stream];
char *audio_buf = NULL;
int c, n, sz = 0;
- if (sub && !dev->input_panic) {
+ if (sub && !cdev->input_panic) {
struct snd_pcm_runtime *rt = sub->runtime;
audio_buf = rt->dma_area;
sz = frames_to_bytes(rt, rt->buffer_size);
/* 3 audio data bytes, followed by 1 check byte */
if (audio_buf) {
for (n = 0; n < BYTES_PER_SAMPLE; n++) {
- audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
+ audio_buf[cdev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
- if (dev->audio_in_buf_pos[stream] == sz)
- dev->audio_in_buf_pos[stream] = 0;
+ if (cdev->audio_in_buf_pos[stream] == sz)
+ cdev->audio_in_buf_pos[stream] = 0;
}
- dev->period_in_count[stream] += BYTES_PER_SAMPLE;
+ cdev->period_in_count[stream] += BYTES_PER_SAMPLE;
}
i += BYTES_PER_SAMPLE;
if (usb_buf[i] != ((stream << 1) | c) &&
- !dev->first_packet) {
- if (!dev->input_panic)
- printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
- ((stream << 1) | c), usb_buf[i], c, stream, i);
- dev->input_panic = 1;
+ !cdev->first_packet) {
+ if (!cdev->input_panic)
+ dev_warn(dev, " EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
+ ((stream << 1) | c), usb_buf[i], c, stream, i);
+ cdev->input_panic = 1;
}
i++;
}
}
- if (dev->first_packet > 0)
- dev->first_packet--;
+ if (cdev->first_packet > 0)
+ cdev->first_packet--;
}
-static void read_in_urb(struct snd_usb_caiaqdev *dev,
+static void read_in_urb(struct snd_usb_caiaqdev *cdev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
- if (!dev->streaming)
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ if (!cdev->streaming)
return;
- if (iso->actual_length < dev->bpp)
+ if (iso->actual_length < cdev->bpp)
return;
- switch (dev->spec.data_alignment) {
+ switch (cdev->spec.data_alignment) {
case 0:
- read_in_urb_mode0(dev, urb, iso);
+ read_in_urb_mode0(cdev, urb, iso);
break;
case 2:
- read_in_urb_mode2(dev, urb, iso);
+ read_in_urb_mode2(cdev, urb, iso);
break;
case 3:
- read_in_urb_mode3(dev, urb, iso);
+ read_in_urb_mode3(cdev, urb, iso);
break;
}
- if ((dev->input_panic || dev->output_panic) && !dev->warned) {
- debug("streaming error detected %s %s\n",
- dev->input_panic ? "(input)" : "",
- dev->output_panic ? "(output)" : "");
- dev->warned = 1;
+ if ((cdev->input_panic || cdev->output_panic) && !cdev->warned) {
+ dev_warn(dev, "streaming error detected %s %s\n",
+ cdev->input_panic ? "(input)" : "",
+ cdev->output_panic ? "(output)" : "");
+ cdev->warned = 1;
}
}
-static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *dev,
+static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *cdev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
int stream, i;
for (i = 0; i < iso->length;) {
- for (stream = 0; stream < dev->n_streams; stream++, i++) {
- sub = dev->sub_playback[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++, i++) {
+ sub = cdev->sub_playback[stream];
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
int sz = frames_to_bytes(rt, rt->buffer_size);
usb_buf[i] =
- audio_buf[dev->audio_out_buf_pos[stream]];
- dev->period_out_count[stream]++;
- dev->audio_out_buf_pos[stream]++;
- if (dev->audio_out_buf_pos[stream] == sz)
- dev->audio_out_buf_pos[stream] = 0;
+ audio_buf[cdev->audio_out_buf_pos[stream]];
+ cdev->period_out_count[stream]++;
+ cdev->audio_out_buf_pos[stream]++;
+ if (cdev->audio_out_buf_pos[stream] == sz)
+ cdev->audio_out_buf_pos[stream] = 0;
} else
usb_buf[i] = 0;
}
/* fill in the check bytes */
- if (dev->spec.data_alignment == 2 &&
- i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
- (dev->n_streams * CHANNELS_PER_STREAM))
- for (stream = 0; stream < dev->n_streams; stream++, i++)
- usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
+ if (cdev->spec.data_alignment == 2 &&
+ i % (cdev->n_streams * BYTES_PER_SAMPLE_USB) ==
+ (cdev->n_streams * CHANNELS_PER_STREAM))
+ for (stream = 0; stream < cdev->n_streams; stream++, i++)
+ usb_buf[i] = MAKE_CHECKBYTE(cdev, stream, i);
}
}
-static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev,
+static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *cdev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
int stream, i;
for (i = 0; i < iso->length;) {
- for (stream = 0; stream < dev->n_streams; stream++) {
- struct snd_pcm_substream *sub = dev->sub_playback[stream];
+ for (stream = 0; stream < cdev->n_streams; stream++) {
+ struct snd_pcm_substream *sub = cdev->sub_playback[stream];
char *audio_buf = NULL;
int c, n, sz = 0;
for (c = 0; c < CHANNELS_PER_STREAM; c++) {
for (n = 0; n < BYTES_PER_SAMPLE; n++) {
if (audio_buf) {
- usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++];
+ usb_buf[i+n] = audio_buf[cdev->audio_out_buf_pos[stream]++];
- if (dev->audio_out_buf_pos[stream] == sz)
- dev->audio_out_buf_pos[stream] = 0;
+ if (cdev->audio_out_buf_pos[stream] == sz)
+ cdev->audio_out_buf_pos[stream] = 0;
} else {
usb_buf[i+n] = 0;
}
}
if (audio_buf)
- dev->period_out_count[stream] += BYTES_PER_SAMPLE;
+ cdev->period_out_count[stream] += BYTES_PER_SAMPLE;
i += BYTES_PER_SAMPLE;
}
}
-static inline void fill_out_urb(struct snd_usb_caiaqdev *dev,
+static inline void fill_out_urb(struct snd_usb_caiaqdev *cdev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
- switch (dev->spec.data_alignment) {
+ switch (cdev->spec.data_alignment) {
case 0:
case 2:
- fill_out_urb_mode_0(dev, urb, iso);
+ fill_out_urb_mode_0(cdev, urb, iso);
break;
case 3:
- fill_out_urb_mode_3(dev, urb, iso);
+ fill_out_urb_mode_3(cdev, urb, iso);
break;
}
}
static void read_completed(struct urb *urb)
{
struct snd_usb_caiaq_cb_info *info = urb->context;
- struct snd_usb_caiaqdev *dev;
+ struct snd_usb_caiaqdev *cdev;
+ struct device *dev;
struct urb *out = NULL;
int i, frame, len, send_it = 0, outframe = 0;
size_t offset = 0;
if (urb->status || !info)
return;
- dev = info->dev;
+ cdev = info->cdev;
+ dev = caiaqdev_to_dev(cdev);
- if (!dev->streaming)
+ if (!cdev->streaming)
return;
/* find an unused output urb that is unused */
for (i = 0; i < N_URBS; i++)
- if (test_and_set_bit(i, &dev->outurb_active_mask) == 0) {
- out = dev->data_urbs_out[i];
+ if (test_and_set_bit(i, &cdev->outurb_active_mask) == 0) {
+ out = cdev->data_urbs_out[i];
break;
}
if (!out) {
- log("Unable to find an output urb to use\n");
+ dev_err(dev, "Unable to find an output urb to use\n");
goto requeue;
}
offset += len;
if (len > 0) {
- spin_lock(&dev->spinlock);
- fill_out_urb(dev, out, &out->iso_frame_desc[outframe]);
- read_in_urb(dev, urb, &urb->iso_frame_desc[frame]);
- spin_unlock(&dev->spinlock);
- check_for_elapsed_periods(dev, dev->sub_playback);
- check_for_elapsed_periods(dev, dev->sub_capture);
+ spin_lock(&cdev->spinlock);
+ fill_out_urb(cdev, out, &out->iso_frame_desc[outframe]);
+ read_in_urb(cdev, urb, &urb->iso_frame_desc[frame]);
+ spin_unlock(&cdev->spinlock);
+ check_for_elapsed_periods(cdev, cdev->sub_playback);
+ check_for_elapsed_periods(cdev, cdev->sub_capture);
send_it = 1;
}
usb_submit_urb(out, GFP_ATOMIC);
} else {
struct snd_usb_caiaq_cb_info *oinfo = out->context;
- clear_bit(oinfo->index, &dev->outurb_active_mask);
+ clear_bit(oinfo->index, &cdev->outurb_active_mask);
}
requeue:
static void write_completed(struct urb *urb)
{
struct snd_usb_caiaq_cb_info *info = urb->context;
- struct snd_usb_caiaqdev *dev = info->dev;
+ struct snd_usb_caiaqdev *cdev = info->cdev;
- if (!dev->output_running) {
- dev->output_running = 1;
- wake_up(&dev->prepare_wait_queue);
+ if (!cdev->output_running) {
+ cdev->output_running = 1;
+ wake_up(&cdev->prepare_wait_queue);
}
- clear_bit(info->index, &dev->outurb_active_mask);
+ clear_bit(info->index, &cdev->outurb_active_mask);
}
-static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret)
+static struct urb **alloc_urbs(struct snd_usb_caiaqdev *cdev, int dir, int *ret)
{
int i, frame;
struct urb **urbs;
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
+ struct device *dev = caiaqdev_to_dev(cdev);
unsigned int pipe;
pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ?
urbs = kmalloc(N_URBS * sizeof(*urbs), GFP_KERNEL);
if (!urbs) {
- log("unable to kmalloc() urbs, OOM!?\n");
+ dev_err(dev, "unable to kmalloc() urbs, OOM!?\n");
*ret = -ENOMEM;
return NULL;
}
for (i = 0; i < N_URBS; i++) {
urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL);
if (!urbs[i]) {
- log("unable to usb_alloc_urb(), OOM!?\n");
+ dev_err(dev, "unable to usb_alloc_urb(), OOM!?\n");
*ret = -ENOMEM;
return urbs;
}
urbs[i]->transfer_buffer =
kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL);
if (!urbs[i]->transfer_buffer) {
- log("unable to kmalloc() transfer buffer, OOM!?\n");
+ dev_err(dev, "unable to kmalloc() transfer buffer, OOM!?\n");
*ret = -ENOMEM;
return urbs;
}
urbs[i]->pipe = pipe;
urbs[i]->transfer_buffer_length = FRAMES_PER_URB
* BYTES_PER_FRAME;
- urbs[i]->context = &dev->data_cb_info[i];
+ urbs[i]->context = &cdev->data_cb_info[i];
urbs[i]->interval = 1;
urbs[i]->transfer_flags = URB_ISO_ASAP;
urbs[i]->number_of_packets = FRAMES_PER_URB;
kfree(urbs);
}
-int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
{
int i, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- dev->n_audio_in = max(dev->spec.num_analog_audio_in,
- dev->spec.num_digital_audio_in) /
+ cdev->n_audio_in = max(cdev->spec.num_analog_audio_in,
+ cdev->spec.num_digital_audio_in) /
CHANNELS_PER_STREAM;
- dev->n_audio_out = max(dev->spec.num_analog_audio_out,
- dev->spec.num_digital_audio_out) /
+ cdev->n_audio_out = max(cdev->spec.num_analog_audio_out,
+ cdev->spec.num_digital_audio_out) /
CHANNELS_PER_STREAM;
- dev->n_streams = max(dev->n_audio_in, dev->n_audio_out);
+ cdev->n_streams = max(cdev->n_audio_in, cdev->n_audio_out);
- debug("dev->n_audio_in = %d\n", dev->n_audio_in);
- debug("dev->n_audio_out = %d\n", dev->n_audio_out);
- debug("dev->n_streams = %d\n", dev->n_streams);
+ dev_dbg(dev, "cdev->n_audio_in = %d\n", cdev->n_audio_in);
+ dev_dbg(dev, "cdev->n_audio_out = %d\n", cdev->n_audio_out);
+ dev_dbg(dev, "cdev->n_streams = %d\n", cdev->n_streams);
- if (dev->n_streams > MAX_STREAMS) {
- log("unable to initialize device, too many streams.\n");
+ if (cdev->n_streams > MAX_STREAMS) {
+ dev_err(dev, "unable to initialize device, too many streams.\n");
return -EINVAL;
}
- ret = snd_pcm_new(dev->chip.card, dev->product_name, 0,
- dev->n_audio_out, dev->n_audio_in, &dev->pcm);
+ ret = snd_pcm_new(cdev->chip.card, cdev->product_name, 0,
+ cdev->n_audio_out, cdev->n_audio_in, &cdev->pcm);
if (ret < 0) {
- log("snd_pcm_new() returned %d\n", ret);
+ dev_err(dev, "snd_pcm_new() returned %d\n", ret);
return ret;
}
- dev->pcm->private_data = dev;
- strlcpy(dev->pcm->name, dev->product_name, sizeof(dev->pcm->name));
+ cdev->pcm->private_data = cdev;
+ strlcpy(cdev->pcm->name, cdev->product_name, sizeof(cdev->pcm->name));
- memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
- memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
+ memset(cdev->sub_playback, 0, sizeof(cdev->sub_playback));
+ memset(cdev->sub_capture, 0, sizeof(cdev->sub_capture));
- memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware,
+ memcpy(&cdev->pcm_info, &snd_usb_caiaq_pcm_hardware,
sizeof(snd_usb_caiaq_pcm_hardware));
/* setup samplerates */
- dev->samplerates = dev->pcm_info.rates;
- switch (dev->chip.usb_id) {
+ cdev->samplerates = cdev->pcm_info.rates;
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE):
- dev->samplerates |= SNDRV_PCM_RATE_192000;
+ cdev->samplerates |= SNDRV_PCM_RATE_192000;
/* fall thru */
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO2DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORAUDIO2):
- dev->samplerates |= SNDRV_PCM_RATE_88200;
+ cdev->samplerates |= SNDRV_PCM_RATE_88200;
break;
}
- snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_usb_caiaq_ops);
- snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
+ snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_usb_caiaq_ops);
- snd_pcm_lib_preallocate_pages_for_all(dev->pcm,
+ snd_pcm_lib_preallocate_pages_for_all(cdev->pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
- dev->data_cb_info =
+ cdev->data_cb_info =
kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS,
GFP_KERNEL);
- if (!dev->data_cb_info)
+ if (!cdev->data_cb_info)
return -ENOMEM;
- dev->outurb_active_mask = 0;
- BUILD_BUG_ON(N_URBS > (sizeof(dev->outurb_active_mask) * 8));
+ cdev->outurb_active_mask = 0;
+ BUILD_BUG_ON(N_URBS > (sizeof(cdev->outurb_active_mask) * 8));
for (i = 0; i < N_URBS; i++) {
- dev->data_cb_info[i].dev = dev;
- dev->data_cb_info[i].index = i;
+ cdev->data_cb_info[i].cdev = cdev;
+ cdev->data_cb_info[i].index = i;
}
- dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret);
+ cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret);
if (ret < 0) {
- kfree(dev->data_cb_info);
- free_urbs(dev->data_urbs_in);
+ kfree(cdev->data_cb_info);
+ free_urbs(cdev->data_urbs_in);
return ret;
}
- dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
+ cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
if (ret < 0) {
- kfree(dev->data_cb_info);
- free_urbs(dev->data_urbs_in);
- free_urbs(dev->data_urbs_out);
+ kfree(cdev->data_cb_info);
+ free_urbs(cdev->data_urbs_in);
+ free_urbs(cdev->data_urbs_out);
return ret;
}
return 0;
}
-void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev)
+void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)
{
- debug("%s(%p)\n", __func__, dev);
- stream_stop(dev);
- free_urbs(dev->data_urbs_in);
- free_urbs(dev->data_urbs_out);
- kfree(dev->data_cb_info);
+ struct device *dev = caiaqdev_to_dev(cdev);
+
+ dev_dbg(dev, "%s(%p)\n", __func__, cdev);
+ stream_stop(cdev);
+ free_urbs(cdev->data_urbs_in);
+ free_urbs(cdev->data_urbs_out);
+ kfree(cdev->data_cb_info);
}
#ifndef CAIAQ_AUDIO_H
#define CAIAQ_AUDIO_H
-int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev);
+int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev);
#endif /* CAIAQ_AUDIO_H */
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <sound/control.h>
struct snd_ctl_elem_info *uinfo)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
int is_intval = pos & CNT_INTVAL;
int maxval = 63;
uinfo->count = 1;
pos &= ~CNT_INTVAL;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
if (pos == 0) {
struct snd_ctl_elem_value *ucontrol)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
if (pos & CNT_INTVAL)
ucontrol->value.integer.value[0]
- = dev->control_state[pos & ~CNT_INTVAL];
+ = cdev->control_state[pos & ~CNT_INTVAL];
else
ucontrol->value.integer.value[0]
- = !!(dev->control_state[pos / 8] & (1 << pos % 8));
+ = !!(cdev->control_state[pos / 8] & (1 << pos % 8));
return 0;
}
struct snd_ctl_elem_value *ucontrol)
{
struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
- struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
+ struct snd_usb_caiaqdev *cdev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
int v = ucontrol->value.integer.value[0];
unsigned char cmd = EP1_CMD_WRITE_IO;
- if (dev->chip.usb_id ==
+ if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1))
cmd = EP1_CMD_DIMM_LEDS;
if (pos & CNT_INTVAL) {
int i = pos & ~CNT_INTVAL;
- dev->control_state[i] = v;
+ cdev->control_state[i] = v;
- if (dev->chip.usb_id ==
+ if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4)) {
int actual_len;
- dev->ep8_out_buf[0] = i;
- dev->ep8_out_buf[1] = v;
+ cdev->ep8_out_buf[0] = i;
+ cdev->ep8_out_buf[1] = v;
- usb_bulk_msg(dev->chip.dev,
- usb_sndbulkpipe(dev->chip.dev, 8),
- dev->ep8_out_buf, sizeof(dev->ep8_out_buf),
+ usb_bulk_msg(cdev->chip.dev,
+ usb_sndbulkpipe(cdev->chip.dev, 8),
+ cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf),
&actual_len, 200);
} else {
- snd_usb_caiaq_send_command(dev, cmd,
- dev->control_state, sizeof(dev->control_state));
+ snd_usb_caiaq_send_command(cdev, cmd,
+ cdev->control_state, sizeof(cdev->control_state));
}
} else {
if (v)
- dev->control_state[pos / 8] |= 1 << (pos % 8);
+ cdev->control_state[pos / 8] |= 1 << (pos % 8);
else
- dev->control_state[pos / 8] &= ~(1 << (pos % 8));
+ cdev->control_state[pos / 8] &= ~(1 << (pos % 8));
- snd_usb_caiaq_send_command(dev, cmd,
- dev->control_state, sizeof(dev->control_state));
+ snd_usb_caiaq_send_command(cdev, cmd,
+ cdev->control_state, sizeof(cdev->control_state));
}
return 1;
};
static int add_controls(struct caiaq_controller *c, int num,
- struct snd_usb_caiaqdev *dev)
+ struct snd_usb_caiaqdev *cdev)
{
int i, ret;
struct snd_kcontrol *kc;
for (i = 0; i < num; i++, c++) {
kcontrol_template.name = c->name;
kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- ret = snd_ctl_add(dev->chip.card, kc);
+ kc = snd_ctl_new1(&kcontrol_template, cdev);
+ ret = snd_ctl_add(cdev->chip.card, kc);
if (ret < 0)
return ret;
}
return 0;
}
-int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev)
{
int ret = 0;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
ret = add_controls(ak1_controller,
- ARRAY_SIZE(ak1_controller), dev);
+ ARRAY_SIZE(ak1_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
ret = add_controls(rk2_controller,
- ARRAY_SIZE(rk2_controller), dev);
+ ARRAY_SIZE(rk2_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
ret = add_controls(rk3_controller,
- ARRAY_SIZE(rk3_controller), dev);
+ ARRAY_SIZE(rk3_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
ret = add_controls(kore_controller,
- ARRAY_SIZE(kore_controller), dev);
+ ARRAY_SIZE(kore_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
ret = add_controls(a8dj_controller,
- ARRAY_SIZE(a8dj_controller), dev);
+ ARRAY_SIZE(a8dj_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
ret = add_controls(a4dj_controller,
- ARRAY_SIZE(a4dj_controller), dev);
+ ARRAY_SIZE(a4dj_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
ret = add_controls(kontrolx1_controller,
- ARRAY_SIZE(kontrolx1_controller), dev);
+ ARRAY_SIZE(kontrolx1_controller), cdev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
ret = add_controls(kontrols4_controller,
- ARRAY_SIZE(kontrols4_controller), dev);
+ ARRAY_SIZE(kontrols4_controller), cdev);
break;
}
#ifndef CAIAQ_CONTROL_H
#define CAIAQ_CONTROL_H
-int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev);
+int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev);
#endif /* CAIAQ_CONTROL_H */
*/
#include <linux/moduleparam.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
static void usb_ep1_command_reply_dispatch (struct urb* urb)
{
int ret;
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct device *dev = &urb->dev->dev;
+ struct snd_usb_caiaqdev *cdev = urb->context;
unsigned char *buf = urb->transfer_buffer;
- if (urb->status || !dev) {
- log("received EP1 urb->status = %i\n", urb->status);
+ if (urb->status || !cdev) {
+ dev_warn(dev, "received EP1 urb->status = %i\n", urb->status);
return;
}
switch(buf[0]) {
case EP1_CMD_GET_DEVICE_INFO:
- memcpy(&dev->spec, buf+1, sizeof(struct caiaq_device_spec));
- dev->spec.fw_version = le16_to_cpu(dev->spec.fw_version);
- debug("device spec (firmware %d): audio: %d in, %d out, "
+ memcpy(&cdev->spec, buf+1, sizeof(struct caiaq_device_spec));
+ cdev->spec.fw_version = le16_to_cpu(cdev->spec.fw_version);
+ dev_dbg(dev, "device spec (firmware %d): audio: %d in, %d out, "
"MIDI: %d in, %d out, data alignment %d\n",
- dev->spec.fw_version,
- dev->spec.num_analog_audio_in,
- dev->spec.num_analog_audio_out,
- dev->spec.num_midi_in,
- dev->spec.num_midi_out,
- dev->spec.data_alignment);
-
- dev->spec_received++;
- wake_up(&dev->ep1_wait_queue);
+ cdev->spec.fw_version,
+ cdev->spec.num_analog_audio_in,
+ cdev->spec.num_analog_audio_out,
+ cdev->spec.num_midi_in,
+ cdev->spec.num_midi_out,
+ cdev->spec.data_alignment);
+
+ cdev->spec_received++;
+ wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_AUDIO_PARAMS:
- dev->audio_parm_answer = buf[1];
- wake_up(&dev->ep1_wait_queue);
+ cdev->audio_parm_answer = buf[1];
+ wake_up(&cdev->ep1_wait_queue);
break;
case EP1_CMD_MIDI_READ:
- snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]);
+ snd_usb_caiaq_midi_handle_input(cdev, buf[1], buf + 3, buf[2]);
break;
case EP1_CMD_READ_IO:
- if (dev->chip.usb_id ==
+ if (cdev->chip.usb_id ==
USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) {
- if (urb->actual_length > sizeof(dev->control_state))
- urb->actual_length = sizeof(dev->control_state);
- memcpy(dev->control_state, buf + 1, urb->actual_length);
- wake_up(&dev->ep1_wait_queue);
+ if (urb->actual_length > sizeof(cdev->control_state))
+ urb->actual_length = sizeof(cdev->control_state);
+ memcpy(cdev->control_state, buf + 1, urb->actual_length);
+ wake_up(&cdev->ep1_wait_queue);
break;
}
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
case EP1_CMD_READ_ERP:
case EP1_CMD_READ_ANALOG:
- snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length);
+ snd_usb_caiaq_input_dispatch(cdev, buf, urb->actual_length);
#endif
break;
}
- dev->ep1_in_urb.actual_length = 0;
- ret = usb_submit_urb(&dev->ep1_in_urb, GFP_ATOMIC);
+ cdev->ep1_in_urb.actual_length = 0;
+ ret = usb_submit_urb(&cdev->ep1_in_urb, GFP_ATOMIC);
if (ret < 0)
- log("unable to submit urb. OOM!?\n");
+ dev_err(dev, "unable to submit urb. OOM!?\n");
}
-int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
unsigned char command,
const unsigned char *buffer,
int len)
{
int actual_len;
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
if (!usb_dev)
return -EIO;
len = EP1_BUFSIZE - 1;
if (buffer && len > 0)
- memcpy(dev->ep1_out_buf+1, buffer, len);
+ memcpy(cdev->ep1_out_buf+1, buffer, len);
- dev->ep1_out_buf[0] = command;
+ cdev->ep1_out_buf[0] = command;
return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
- dev->ep1_out_buf, len+1, &actual_len, 200);
+ cdev->ep1_out_buf, len+1, &actual_len, 200);
}
-int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev,
int rate, int depth, int bpp)
{
int ret;
char tmp[5];
+ struct device *dev = caiaqdev_to_dev(cdev);
switch (rate) {
case 44100: tmp[0] = SAMPLERATE_44100; break;
tmp[3] = bpp >> 8;
tmp[4] = 1; /* packets per microframe */
- debug("setting audio params: %d Hz, %d bits, %d bpp\n",
+ dev_dbg(dev, "setting audio params: %d Hz, %d bits, %d bpp\n",
rate, depth, bpp);
- dev->audio_parm_answer = -1;
- ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS,
+ cdev->audio_parm_answer = -1;
+ ret = snd_usb_caiaq_send_command(cdev, EP1_CMD_AUDIO_PARAMS,
tmp, sizeof(tmp));
if (ret)
return ret;
- if (!wait_event_timeout(dev->ep1_wait_queue,
- dev->audio_parm_answer >= 0, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue,
+ cdev->audio_parm_answer >= 0, HZ))
return -EPIPE;
- if (dev->audio_parm_answer != 1)
- debug("unable to set the device's audio params\n");
+ if (cdev->audio_parm_answer != 1)
+ dev_dbg(dev, "unable to set the device's audio params\n");
else
- dev->bpp = bpp;
+ cdev->bpp = bpp;
- return dev->audio_parm_answer == 1 ? 0 : -EINVAL;
+ return cdev->audio_parm_answer == 1 ? 0 : -EINVAL;
}
-int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *cdev,
int digital, int analog, int erp)
{
char tmp[3] = { digital, analog, erp };
- return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
+ return snd_usb_caiaq_send_command(cdev, EP1_CMD_AUTO_MSG,
tmp, sizeof(tmp));
}
-static void setup_card(struct snd_usb_caiaqdev *dev)
+static void setup_card(struct snd_usb_caiaqdev *cdev)
{
int ret;
char val[4];
+ struct device *dev = caiaqdev_to_dev(cdev);
/* device-specific startup specials */
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
/* RigKontrol2 - display centered dash ('-') */
val[0] = 0x00;
val[1] = 0x00;
val[2] = 0x01;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 3);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
/* RigKontrol2 - display two centered dashes ('--') */
val[1] = 0x40;
val[2] = 0x40;
val[3] = 0x00;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 4);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
/* Audio Kontrol 1 - make USB-LED stop blinking */
val[0] = 0x00;
- snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_WRITE_IO, val, 1);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
/* Audio 8 DJ - trigger read of current settings */
- dev->control_state[0] = 0xff;
- snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0);
- snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0);
+ cdev->control_state[0] = 0xff;
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 0, 0);
+ snd_usb_caiaq_send_command(cdev, EP1_CMD_READ_IO, NULL, 0);
- if (!wait_event_timeout(dev->ep1_wait_queue,
- dev->control_state[0] != 0xff, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue,
+ cdev->control_state[0] != 0xff, HZ))
return;
/* fix up some defaults */
- if ((dev->control_state[1] != 2) ||
- (dev->control_state[2] != 3) ||
- (dev->control_state[4] != 2)) {
- dev->control_state[1] = 2;
- dev->control_state[2] = 3;
- dev->control_state[4] = 2;
- snd_usb_caiaq_send_command(dev,
- EP1_CMD_WRITE_IO, dev->control_state, 6);
+ if ((cdev->control_state[1] != 2) ||
+ (cdev->control_state[2] != 3) ||
+ (cdev->control_state[4] != 2)) {
+ cdev->control_state[1] = 2;
+ cdev->control_state[2] = 3;
+ cdev->control_state[4] = 2;
+ snd_usb_caiaq_send_command(cdev,
+ EP1_CMD_WRITE_IO, cdev->control_state, 6);
}
break;
}
- if (dev->spec.num_analog_audio_out +
- dev->spec.num_analog_audio_in +
- dev->spec.num_digital_audio_out +
- dev->spec.num_digital_audio_in > 0) {
- ret = snd_usb_caiaq_audio_init(dev);
+ if (cdev->spec.num_analog_audio_out +
+ cdev->spec.num_analog_audio_in +
+ cdev->spec.num_digital_audio_out +
+ cdev->spec.num_digital_audio_in > 0) {
+ ret = snd_usb_caiaq_audio_init(cdev);
if (ret < 0)
- log("Unable to set up audio system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up audio system (ret=%d)\n", ret);
}
- if (dev->spec.num_midi_in +
- dev->spec.num_midi_out > 0) {
- ret = snd_usb_caiaq_midi_init(dev);
+ if (cdev->spec.num_midi_in +
+ cdev->spec.num_midi_out > 0) {
+ ret = snd_usb_caiaq_midi_init(cdev);
if (ret < 0)
- log("Unable to set up MIDI system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up MIDI system (ret=%d)\n", ret);
}
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
- ret = snd_usb_caiaq_input_init(dev);
+ ret = snd_usb_caiaq_input_init(cdev);
if (ret < 0)
- log("Unable to set up input system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up input system (ret=%d)\n", ret);
#endif
/* finally, register the card and all its sub-instances */
- ret = snd_card_register(dev->chip.card);
+ ret = snd_card_register(cdev->chip.card);
if (ret < 0) {
- log("snd_card_register() returned %d\n", ret);
- snd_card_free(dev->chip.card);
+ dev_err(dev, "snd_card_register() returned %d\n", ret);
+ snd_card_free(cdev->chip.card);
}
- ret = snd_usb_caiaq_control_init(dev);
+ ret = snd_usb_caiaq_control_init(cdev);
if (ret < 0)
- log("Unable to set up control system (ret=%d)\n", ret);
+ dev_err(dev, "Unable to set up control system (ret=%d)\n", ret);
}
static int create_card(struct usb_device *usb_dev,
int devnum;
int err;
struct snd_card *card;
- struct snd_usb_caiaqdev *dev;
+ struct snd_usb_caiaqdev *cdev;
for (devnum = 0; devnum < SNDRV_CARDS; devnum++)
if (enable[devnum] && !snd_card_used[devnum])
if (err < 0)
return err;
- dev = caiaqdev(card);
- dev->chip.dev = usb_dev;
- dev->chip.card = card;
- dev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
+ cdev = caiaqdev(card);
+ cdev->chip.dev = usb_dev;
+ cdev->chip.card = card;
+ cdev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct));
- spin_lock_init(&dev->spinlock);
+ spin_lock_init(&cdev->spinlock);
snd_card_set_dev(card, &intf->dev);
*cardp = card;
return 0;
}
-static int init_card(struct snd_usb_caiaqdev *dev)
+static int init_card(struct snd_usb_caiaqdev *cdev)
{
char *c, usbpath[32];
- struct usb_device *usb_dev = dev->chip.dev;
- struct snd_card *card = dev->chip.card;
+ struct usb_device *usb_dev = cdev->chip.dev;
+ struct snd_card *card = cdev->chip.card;
+ struct device *dev = caiaqdev_to_dev(cdev);
int err, len;
if (usb_set_interface(usb_dev, 0, 1) != 0) {
- log("can't set alt interface.\n");
+ dev_err(dev, "can't set alt interface.\n");
return -EIO;
}
- usb_init_urb(&dev->ep1_in_urb);
- usb_init_urb(&dev->midi_out_urb);
+ usb_init_urb(&cdev->ep1_in_urb);
+ usb_init_urb(&cdev->midi_out_urb);
- usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev,
+ usb_fill_bulk_urb(&cdev->ep1_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x1),
- dev->ep1_in_buf, EP1_BUFSIZE,
- usb_ep1_command_reply_dispatch, dev);
+ cdev->ep1_in_buf, EP1_BUFSIZE,
+ usb_ep1_command_reply_dispatch, cdev);
- usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev,
+ usb_fill_bulk_urb(&cdev->midi_out_urb, usb_dev,
usb_sndbulkpipe(usb_dev, 0x1),
- dev->midi_out_buf, EP1_BUFSIZE,
- snd_usb_caiaq_midi_output_done, dev);
+ cdev->midi_out_buf, EP1_BUFSIZE,
+ snd_usb_caiaq_midi_output_done, cdev);
- init_waitqueue_head(&dev->ep1_wait_queue);
- init_waitqueue_head(&dev->prepare_wait_queue);
+ init_waitqueue_head(&cdev->ep1_wait_queue);
+ init_waitqueue_head(&cdev->prepare_wait_queue);
- if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
+ if (usb_submit_urb(&cdev->ep1_in_urb, GFP_KERNEL) != 0)
return -EIO;
- err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
+ err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
if (err)
return err;
- if (!wait_event_timeout(dev->ep1_wait_queue, dev->spec_received, HZ))
+ if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ))
return -ENODEV;
usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
- dev->vendor_name, CAIAQ_USB_STR_LEN);
+ cdev->vendor_name, CAIAQ_USB_STR_LEN);
usb_string(usb_dev, usb_dev->descriptor.iProduct,
- dev->product_name, CAIAQ_USB_STR_LEN);
+ cdev->product_name, CAIAQ_USB_STR_LEN);
strlcpy(card->driver, MODNAME, sizeof(card->driver));
- strlcpy(card->shortname, dev->product_name, sizeof(card->shortname));
- strlcpy(card->mixername, dev->product_name, sizeof(card->mixername));
+ strlcpy(card->shortname, cdev->product_name, sizeof(card->shortname));
+ strlcpy(card->mixername, cdev->product_name, sizeof(card->mixername));
/* if the id was not passed as module option, fill it with a shortened
* version of the product string which does not contain any
}
usb_make_path(usb_dev, usbpath, sizeof(usbpath));
- snprintf(card->longname, sizeof(card->longname),
- "%s %s (%s)",
- dev->vendor_name, dev->product_name, usbpath);
+ snprintf(card->longname, sizeof(card->longname), "%s %s (%s)",
+ cdev->vendor_name, cdev->product_name, usbpath);
- setup_card(dev);
+ setup_card(cdev);
return 0;
}
{
int ret;
struct snd_card *card = NULL;
- struct usb_device *device = interface_to_usbdev(intf);
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
- ret = create_card(device, intf, &card);
+ ret = create_card(usb_dev, intf, &card);
if (ret < 0)
return ret;
usb_set_intfdata(intf, card);
ret = init_card(caiaqdev(card));
if (ret < 0) {
- log("unable to init card! (ret=%d)\n", ret);
+ dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret);
snd_card_free(card);
return ret;
}
static void snd_disconnect(struct usb_interface *intf)
{
- struct snd_usb_caiaqdev *dev;
struct snd_card *card = usb_get_intfdata(intf);
-
- debug("%s(%p)\n", __func__, intf);
+ struct device *dev = intf->usb_dev;
+ struct snd_usb_caiaqdev *cdev;
if (!card)
return;
- dev = caiaqdev(card);
+ cdev = caiaqdev(card);
+ dev_dbg(dev, "%s(%p)\n", __func__, intf);
+
snd_card_disconnect(card);
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
- snd_usb_caiaq_input_free(dev);
+ snd_usb_caiaq_input_free(cdev);
#endif
- snd_usb_caiaq_audio_free(dev);
+ snd_usb_caiaq_audio_free(cdev);
- usb_kill_urb(&dev->ep1_in_urb);
- usb_kill_urb(&dev->midi_out_urb);
+ usb_kill_urb(&cdev->ep1_in_urb);
+ usb_kill_urb(&cdev->midi_out_urb);
snd_card_free(card);
usb_reset_device(interface_to_usbdev(intf));
};
module_usb_driver(snd_usb_driver);
-
#define CAIAQ_USB_STR_LEN 0xff
#define MAX_STREAMS 32
-//#define SND_USB_CAIAQ_DEBUG
-
#define MODNAME "snd-usb-caiaq"
-#define log(x...) snd_printk(KERN_WARNING MODNAME" log: " x)
-
-#ifdef SND_USB_CAIAQ_DEBUG
-#define debug(x...) snd_printk(KERN_WARNING MODNAME " debug: " x)
-#else
-#define debug(x...) do { } while(0)
-#endif
#define EP1_CMD_GET_DEVICE_INFO 0x1
#define EP1_CMD_READ_ERP 0x2
};
struct snd_usb_caiaq_cb_info {
- struct snd_usb_caiaqdev *dev;
+ struct snd_usb_caiaqdev *cdev;
int index;
};
#define caiaqdev(c) ((struct snd_usb_caiaqdev*)(c)->private_data)
+#define caiaqdev_to_dev(d) (d->chip.card->dev)
-int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp);
-int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp);
-int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev,
+int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev, int rate, int depth, int bbp);
+int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *cdev, int digital, int analog, int erp);
+int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev,
unsigned char command,
const unsigned char *buffer,
int len);
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/usb.h>
#undef HIGH_PEAK
#undef LOW_PEAK
-static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *dev,
+static inline void snd_caiaq_input_report_abs(struct snd_usb_caiaqdev *cdev,
int axis, const unsigned char *buf,
int offset)
{
- input_report_abs(dev->input_dev, axis,
+ input_report_abs(cdev->input_dev, axis,
(buf[offset * 2] << 8) | buf[offset * 2 + 1]);
}
-static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *cdev,
const unsigned char *buf,
unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
- snd_caiaq_input_report_abs(dev, ABS_X, buf, 2);
- snd_caiaq_input_report_abs(dev, ABS_Y, buf, 0);
- snd_caiaq_input_report_abs(dev, ABS_Z, buf, 1);
+ snd_caiaq_input_report_abs(cdev, ABS_X, buf, 2);
+ snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 1);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- snd_caiaq_input_report_abs(dev, ABS_X, buf, 0);
- snd_caiaq_input_report_abs(dev, ABS_Y, buf, 1);
- snd_caiaq_input_report_abs(dev, ABS_Z, buf, 2);
+ snd_caiaq_input_report_abs(cdev, ABS_X, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_Y, buf, 1);
+ snd_caiaq_input_report_abs(cdev, ABS_Z, buf, 2);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
- snd_caiaq_input_report_abs(dev, ABS_HAT0X, buf, 4);
- snd_caiaq_input_report_abs(dev, ABS_HAT0Y, buf, 2);
- snd_caiaq_input_report_abs(dev, ABS_HAT1X, buf, 6);
- snd_caiaq_input_report_abs(dev, ABS_HAT1Y, buf, 1);
- snd_caiaq_input_report_abs(dev, ABS_HAT2X, buf, 7);
- snd_caiaq_input_report_abs(dev, ABS_HAT2Y, buf, 0);
- snd_caiaq_input_report_abs(dev, ABS_HAT3X, buf, 5);
- snd_caiaq_input_report_abs(dev, ABS_HAT3Y, buf, 3);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT0X, buf, 4);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT0Y, buf, 2);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT1X, buf, 6);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT1Y, buf, 1);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT2X, buf, 7);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT2Y, buf, 0);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT3X, buf, 5);
+ snd_caiaq_input_report_abs(cdev, ABS_HAT3Y, buf, 3);
break;
}
input_sync(input_dev);
}
-static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev,
const char *buf, unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
int i;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
i = decode_erp(buf[0], buf[1]);
input_report_abs(input_dev, ABS_X, i);
}
}
-static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
+static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *cdev,
unsigned char *buf, unsigned int len)
{
- struct input_dev *input_dev = dev->input_dev;
+ struct input_dev *input_dev = cdev->input_dev;
unsigned short *keycode = input_dev->keycode;
int i;
input_report_key(input_dev, keycode[i],
buf[i / 8] & (1 << (i % 8)));
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
+ input_report_abs(cdev->input_dev, ABS_MISC, 255 - buf[4]);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
/* rotary encoders */
- input_report_abs(dev->input_dev, ABS_X, buf[5] & 0xf);
- input_report_abs(dev->input_dev, ABS_Y, buf[5] >> 4);
- input_report_abs(dev->input_dev, ABS_Z, buf[6] & 0xf);
- input_report_abs(dev->input_dev, ABS_MISC, buf[6] >> 4);
+ input_report_abs(cdev->input_dev, ABS_X, buf[5] & 0xf);
+ input_report_abs(cdev->input_dev, ABS_Y, buf[5] >> 4);
+ input_report_abs(cdev->input_dev, ABS_Z, buf[6] & 0xf);
+ input_report_abs(cdev->input_dev, ABS_MISC, buf[6] >> 4);
break;
}
#define TKS4_MSGBLOCK_SIZE 16
-static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev,
+static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev,
const unsigned char *buf,
unsigned int len)
{
+ struct device *dev = caiaqdev_to_dev(cdev);
+
while (len) {
unsigned int i, block_id = (buf[0] << 8) | buf[1];
case 0:
/* buttons */
for (i = 0; i < KONTROLS4_BUTTONS; i++)
- input_report_key(dev->input_dev, KONTROLS4_BUTTON(i),
+ input_report_key(cdev->input_dev, KONTROLS4_BUTTON(i),
(buf[4 + (i / 8)] >> (i % 8)) & 1);
break;
case 1:
/* left wheel */
- input_report_abs(dev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8));
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(36), buf[9] | ((buf[8] & 0x3) << 8));
/* right wheel */
- input_report_abs(dev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8));
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(37), buf[13] | ((buf[12] & 0x3) << 8));
/* rotary encoders */
- input_report_abs(dev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4);
- input_report_abs(dev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(38), buf[3] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(39), buf[4] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(40), buf[4] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(41), buf[5] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(42), buf[5] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(43), buf[6] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(44), buf[6] & 0xf);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(45), buf[7] >> 4);
+ input_report_abs(cdev->input_dev, KONTROLS4_ABS(46), buf[7] & 0xf);
break;
case 2:
/* Volume Fader Channel D */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(0), buf, 1);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(0), buf, 1);
/* Volume Fader Channel B */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(1), buf, 2);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(1), buf, 2);
/* Volume Fader Channel A */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(2), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(2), buf, 3);
/* Volume Fader Channel C */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(3), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(3), buf, 4);
/* Loop Volume */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(4), buf, 6);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(4), buf, 6);
/* Crossfader */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(7), buf, 7);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(7), buf, 7);
break;
case 3:
/* Tempo Fader R */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(6), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(6), buf, 3);
/* Tempo Fader L */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(5), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(5), buf, 4);
/* Mic Volume */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(8), buf, 6);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(8), buf, 6);
/* Cue Mix */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(9), buf, 7);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(9), buf, 7);
break;
case 4:
/* Wheel distance sensor L */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(10), buf, 1);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(10), buf, 1);
/* Wheel distance sensor R */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(11), buf, 2);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(11), buf, 2);
/* Channel D EQ - Filter */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(12), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(12), buf, 3);
/* Channel D EQ - Low */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(13), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(13), buf, 4);
/* Channel D EQ - Mid */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(14), buf, 5);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(14), buf, 5);
/* Channel D EQ - Hi */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(15), buf, 6);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(15), buf, 6);
/* FX2 - dry/wet */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(16), buf, 7);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(16), buf, 7);
break;
case 5:
/* FX2 - 1 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(17), buf, 1);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(17), buf, 1);
/* FX2 - 2 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(18), buf, 2);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(18), buf, 2);
/* FX2 - 3 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(19), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(19), buf, 3);
/* Channel B EQ - Filter */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(20), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(20), buf, 4);
/* Channel B EQ - Low */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(21), buf, 5);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(21), buf, 5);
/* Channel B EQ - Mid */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(22), buf, 6);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(22), buf, 6);
/* Channel B EQ - Hi */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(23), buf, 7);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(23), buf, 7);
break;
case 6:
/* Channel A EQ - Filter */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(24), buf, 1);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(24), buf, 1);
/* Channel A EQ - Low */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(25), buf, 2);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(25), buf, 2);
/* Channel A EQ - Mid */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(26), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(26), buf, 3);
/* Channel A EQ - Hi */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(27), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(27), buf, 4);
/* Channel C EQ - Filter */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(28), buf, 5);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(28), buf, 5);
/* Channel C EQ - Low */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(29), buf, 6);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(29), buf, 6);
/* Channel C EQ - Mid */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(30), buf, 7);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(30), buf, 7);
break;
case 7:
/* Channel C EQ - Hi */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(31), buf, 1);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(31), buf, 1);
/* FX1 - wet/dry */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(32), buf, 2);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(32), buf, 2);
/* FX1 - 1 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(33), buf, 3);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(33), buf, 3);
/* FX1 - 2 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(34), buf, 4);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(34), buf, 4);
/* FX1 - 3 */
- snd_caiaq_input_report_abs(dev, KONTROLS4_ABS(35), buf, 5);
+ snd_caiaq_input_report_abs(cdev, KONTROLS4_ABS(35), buf, 5);
break;
default:
- debug("%s(): bogus block (id %d)\n",
+ dev_dbg(dev, "%s(): bogus block (id %d)\n",
__func__, block_id);
return;
}
buf += TKS4_MSGBLOCK_SIZE;
}
- input_sync(dev->input_dev);
+ input_sync(cdev->input_dev);
}
#define MASCHINE_MSGBLOCK_SIZE 2
-static void snd_usb_caiaq_maschine_dispatch(struct snd_usb_caiaqdev *dev,
+static void snd_usb_caiaq_maschine_dispatch(struct snd_usb_caiaqdev *cdev,
const unsigned char *buf,
unsigned int len)
{
pressure = be16_to_cpu(buf[i * 2] << 8 | buf[(i * 2) + 1]);
pad_id = pressure >> 12;
- input_report_abs(dev->input_dev, MASCHINE_PAD(pad_id), pressure & 0xfff);
+ input_report_abs(cdev->input_dev, MASCHINE_PAD(pad_id), pressure & 0xfff);
}
- input_sync(dev->input_dev);
+ input_sync(cdev->input_dev);
}
static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
{
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct snd_usb_caiaqdev *cdev = urb->context;
unsigned char *buf = urb->transfer_buffer;
+ struct device *dev = &urb->dev->dev;
int ret;
- if (urb->status || !dev || urb != dev->ep4_in_urb)
+ if (urb->status || !cdev || urb != cdev->ep4_in_urb)
return;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
if (urb->actual_length < 24)
goto requeue;
if (buf[0] & 0x3)
- snd_caiaq_input_read_io(dev, buf + 1, 7);
+ snd_caiaq_input_read_io(cdev, buf + 1, 7);
if (buf[0] & 0x4)
- snd_caiaq_input_read_analog(dev, buf + 8, 16);
+ snd_caiaq_input_read_analog(cdev, buf + 8, 16);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
- snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length);
+ snd_usb_caiaq_tks4_dispatch(cdev, buf, urb->actual_length);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
if (urb->actual_length < (MASCHINE_PADS * MASCHINE_MSGBLOCK_SIZE))
goto requeue;
- snd_usb_caiaq_maschine_dispatch(dev, buf, urb->actual_length);
+ snd_usb_caiaq_maschine_dispatch(cdev, buf, urb->actual_length);
break;
}
requeue:
- dev->ep4_in_urb->actual_length = 0;
- ret = usb_submit_urb(dev->ep4_in_urb, GFP_ATOMIC);
+ cdev->ep4_in_urb->actual_length = 0;
+ ret = usb_submit_urb(cdev->ep4_in_urb, GFP_ATOMIC);
if (ret < 0)
- log("unable to submit urb. OOM!?\n");
+ dev_err(dev, "unable to submit urb. OOM!?\n");
}
static int snd_usb_caiaq_input_open(struct input_dev *idev)
{
- struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+ struct snd_usb_caiaqdev *cdev = input_get_drvdata(idev);
- if (!dev)
+ if (!cdev)
return -EINVAL;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
- if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
+ if (usb_submit_urb(cdev->ep4_in_urb, GFP_KERNEL) != 0)
return -EIO;
break;
}
static void snd_usb_caiaq_input_close(struct input_dev *idev)
{
- struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+ struct snd_usb_caiaqdev *cdev = input_get_drvdata(idev);
- if (!dev)
+ if (!cdev)
return;
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
- usb_kill_urb(dev->ep4_in_urb);
+ usb_kill_urb(cdev->ep4_in_urb);
break;
}
}
-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
+void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev,
char *buf,
unsigned int len)
{
- if (!dev->input_dev || len < 1)
+ if (!cdev->input_dev || len < 1)
return;
switch (buf[0]) {
case EP1_CMD_READ_ANALOG:
- snd_caiaq_input_read_analog(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_analog(cdev, buf + 1, len - 1);
break;
case EP1_CMD_READ_ERP:
- snd_caiaq_input_read_erp(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_erp(cdev, buf + 1, len - 1);
break;
case EP1_CMD_READ_IO:
- snd_caiaq_input_read_io(dev, buf + 1, len - 1);
+ snd_caiaq_input_read_io(cdev, buf + 1, len - 1);
break;
}
}
-int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
+int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev)
{
- struct usb_device *usb_dev = dev->chip.dev;
+ struct usb_device *usb_dev = cdev->chip.dev;
struct input_dev *input;
int i, ret = 0;
if (!input)
return -ENOMEM;
- usb_make_path(usb_dev, dev->phys, sizeof(dev->phys));
- strlcat(dev->phys, "/input0", sizeof(dev->phys));
+ usb_make_path(usb_dev, cdev->phys, sizeof(cdev->phys));
+ strlcat(cdev->phys, "/input0", sizeof(cdev->phys));
- input->name = dev->product_name;
- input->phys = dev->phys;
+ input->name = cdev->product_name;
+ input->phys = cdev->phys;
usb_to_input_id(usb_dev, &input->id);
input->dev.parent = &usb_dev->dev;
- input_set_drvdata(input, dev);
+ input_set_drvdata(input, cdev);
- switch (dev->chip.usb_id) {
+ switch (cdev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2));
- memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_rk2));
+ memcpy(cdev->keycode, keycode_rk2, sizeof(keycode_rk2));
input->keycodemax = ARRAY_SIZE(keycode_rk2);
input_set_abs_params(input, ABS_X, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 0);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3));
- memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_rk3));
+ memcpy(cdev->keycode, keycode_rk3, sizeof(keycode_rk3));
input->keycodemax = ARRAY_SIZE(keycode_rk3);
input_set_abs_params(input, ABS_X, 0, 1024, 0, 10);
input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 0);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->absbit[0] = BIT_MASK(ABS_X);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1));
- memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_ak1));
+ memcpy(cdev->keycode, keycode_ak1, sizeof(keycode_ak1));
input->keycodemax = ARRAY_SIZE(keycode_ak1);
input_set_abs_params(input, ABS_X, 0, 999, 0, 10);
- snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 0, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_kore));
- memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_kore));
+ memcpy(cdev->keycode, keycode_kore, sizeof(keycode_kore));
input->keycodemax = ARRAY_SIZE(keycode_kore);
input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10);
input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
BIT_MASK(ABS_Z);
input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
- BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLX1_INPUTS);
+ BUILD_BUG_ON(sizeof(cdev->keycode) < KONTROLX1_INPUTS);
for (i = 0; i < KONTROLX1_INPUTS; i++)
- dev->keycode[i] = BTN_MISC + i;
+ cdev->keycode[i] = BTN_MISC + i;
input->keycodemax = KONTROLX1_INPUTS;
/* analog potentiometers */
input_set_abs_params(input, ABS_Z, 0, 0xf, 0, 1);
input_set_abs_params(input, ABS_MISC, 0, 0xf, 0, 1);
- dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->ep4_in_urb) {
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
ret = -ENOMEM;
goto exit_free_idev;
}
- usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x4),
- dev->ep4_in_buf, EP4_BUFSIZE,
- snd_usb_caiaq_ep4_reply_dispatch, dev);
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLS4_BUTTONS);
+ BUILD_BUG_ON(sizeof(cdev->keycode) < KONTROLS4_BUTTONS);
for (i = 0; i < KONTROLS4_BUTTONS; i++)
- dev->keycode[i] = KONTROLS4_BUTTON(i);
+ cdev->keycode[i] = KONTROLS4_BUTTON(i);
input->keycodemax = KONTROLS4_BUTTONS;
for (i = 0; i < KONTROLS4_AXIS; i++) {
for (i = 0; i < 9; i++)
input_set_abs_params(input, KONTROLS4_ABS(38+i), 0, 0xf, 0, 1);
- dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->ep4_in_urb) {
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
ret = -ENOMEM;
goto exit_free_idev;
}
- usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x4),
- dev->ep4_in_buf, EP4_BUFSIZE,
- snd_usb_caiaq_ep4_reply_dispatch, dev);
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
BIT_MASK(ABS_RX) | BIT_MASK(ABS_RY) |
BIT_MASK(ABS_RZ);
- BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_maschine));
- memcpy(dev->keycode, keycode_maschine, sizeof(keycode_maschine));
+ BUILD_BUG_ON(sizeof(cdev->keycode) < sizeof(keycode_maschine));
+ memcpy(cdev->keycode, keycode_maschine, sizeof(keycode_maschine));
input->keycodemax = ARRAY_SIZE(keycode_maschine);
for (i = 0; i < MASCHINE_PADS; i++) {
input_set_abs_params(input, ABS_RY, 0, 999, 0, 10);
input_set_abs_params(input, ABS_RZ, 0, 999, 0, 10);
- dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->ep4_in_urb) {
+ cdev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cdev->ep4_in_urb) {
ret = -ENOMEM;
goto exit_free_idev;
}
- usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+ usb_fill_bulk_urb(cdev->ep4_in_urb, usb_dev,
usb_rcvbulkpipe(usb_dev, 0x4),
- dev->ep4_in_buf, EP4_BUFSIZE,
- snd_usb_caiaq_ep4_reply_dispatch, dev);
+ cdev->ep4_in_buf, EP4_BUFSIZE,
+ snd_usb_caiaq_ep4_reply_dispatch, cdev);
- snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+ snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5);
break;
default:
input->open = snd_usb_caiaq_input_open;
input->close = snd_usb_caiaq_input_close;
- input->keycode = dev->keycode;
+ input->keycode = cdev->keycode;
input->keycodesize = sizeof(unsigned short);
for (i = 0; i < input->keycodemax; i++)
- __set_bit(dev->keycode[i], input->keybit);
+ __set_bit(cdev->keycode[i], input->keybit);
- dev->input_dev = input;
+ cdev->input_dev = input;
ret = input_register_device(input);
if (ret < 0)
exit_free_idev:
input_free_device(input);
- dev->input_dev = NULL;
+ cdev->input_dev = NULL;
return ret;
}
-void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
+void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev)
{
- if (!dev || !dev->input_dev)
+ if (!cdev || !cdev->input_dev)
return;
- usb_kill_urb(dev->ep4_in_urb);
- usb_free_urb(dev->ep4_in_urb);
- dev->ep4_in_urb = NULL;
+ usb_kill_urb(cdev->ep4_in_urb);
+ usb_free_urb(cdev->ep4_in_urb);
+ cdev->ep4_in_urb = NULL;
- input_unregister_device(dev->input_dev);
- dev->input_dev = NULL;
+ input_unregister_device(cdev->input_dev);
+ cdev->input_dev = NULL;
}
#ifndef CAIAQ_INPUT_H
#define CAIAQ_INPUT_H
-void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len);
-int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev);
+void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *cdev, char *buf, unsigned int len);
+int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *cdev);
#endif
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/device.h>
#include <linux/usb.h>
#include <linux/gfp.h>
#include <sound/rawmidi.h>
static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
- if (!dev)
+ if (!cdev)
return;
- dev->midi_receive_substream = up ? substream : NULL;
+ cdev->midi_receive_substream = up ? substream : NULL;
}
static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substream)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
- if (dev->midi_out_active) {
- usb_kill_urb(&dev->midi_out_urb);
- dev->midi_out_active = 0;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
+ if (cdev->midi_out_active) {
+ usb_kill_urb(&cdev->midi_out_urb);
+ cdev->midi_out_active = 0;
}
return 0;
}
-static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev,
+static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev,
struct snd_rawmidi_substream *substream)
{
int len, ret;
+ struct device *dev = caiaqdev_to_dev(cdev);
- dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
- dev->midi_out_buf[1] = 0; /* port */
- len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3,
+ cdev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
+ cdev->midi_out_buf[1] = 0; /* port */
+ len = snd_rawmidi_transmit(substream, cdev->midi_out_buf + 3,
EP1_BUFSIZE - 3);
if (len <= 0)
return;
- dev->midi_out_buf[2] = len;
- dev->midi_out_urb.transfer_buffer_length = len+3;
+ cdev->midi_out_buf[2] = len;
+ cdev->midi_out_urb.transfer_buffer_length = len+3;
- ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC);
+ ret = usb_submit_urb(&cdev->midi_out_urb, GFP_ATOMIC);
if (ret < 0)
- log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
- "ret=%d, len=%d\n",
- substream, ret, len);
+ dev_err(dev,
+ "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
+ "ret=%d, len=%d\n", substream, ret, len);
else
- dev->midi_out_active = 1;
+ cdev->midi_out_active = 1;
}
static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
+ struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
if (up) {
- dev->midi_out_substream = substream;
- if (!dev->midi_out_active)
- snd_usb_caiaq_midi_send(dev, substream);
+ cdev->midi_out_substream = substream;
+ if (!cdev->midi_out_active)
+ snd_usb_caiaq_midi_send(cdev, substream);
} else {
- dev->midi_out_substream = NULL;
+ cdev->midi_out_substream = NULL;
}
}
.trigger = snd_usb_caiaq_midi_input_trigger,
};
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *cdev,
int port, const char *buf, int len)
{
- if (!dev->midi_receive_substream)
+ if (!cdev->midi_receive_substream)
return;
- snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
+ snd_rawmidi_receive(cdev->midi_receive_substream, buf, len);
}
int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device)
void snd_usb_caiaq_midi_output_done(struct urb* urb)
{
- struct snd_usb_caiaqdev *dev = urb->context;
+ struct snd_usb_caiaqdev *cdev = urb->context;
- dev->midi_out_active = 0;
+ cdev->midi_out_active = 0;
if (urb->status != 0)
return;
- if (!dev->midi_out_substream)
+ if (!cdev->midi_out_substream)
return;
- snd_usb_caiaq_midi_send(dev, dev->midi_out_substream);
+ snd_usb_caiaq_midi_send(cdev, cdev->midi_out_substream);
}
-
#ifndef CAIAQ_MIDI_H
#define CAIAQ_MIDI_H
-int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *dev);
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, int port, const char *buf, int len);
+int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *cdev);
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *cdev,
+ int port, const char *buf, int len);
void snd_usb_caiaq_midi_output_done(struct urb *urb);
#endif /* CAIAQ_MIDI_H */
static int nrpacks = 8; /* max. number of packets per urb */
static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
static bool ignore_ctl_error;
+static bool autoclock = true;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
module_param(ignore_ctl_error, bool, 0444);
MODULE_PARM_DESC(ignore_ctl_error,
"Ignore errors from USB controller for mixer interfaces.");
+module_param(autoclock, bool, 0444);
+MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
chip->card = card;
chip->setup = device_setup[idx];
chip->nrpacks = nrpacks;
+ chip->autoclock = autoclock;
chip->probing = 1;
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
{
struct snd_usb_audio *chip = usb_get_intfdata(intf);
- struct list_head *p;
struct snd_usb_stream *as;
struct usb_mixer_interface *mixer;
if (!PMSG_IS_AUTO(message)) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
if (!chip->num_suspended_intf++) {
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
+ list_for_each_entry(as, &chip->pcm_list, list) {
snd_pcm_suspend_all(as->pcm);
as->substream[0].need_setup_ep =
as->substream[1].need_setup_ep = true;
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
{ } /* Terminating entry */
};
-
-MODULE_DEVICE_TABLE (usb, usb_audio_ids);
+MODULE_DEVICE_TABLE(usb, usb_audio_ids);
/*
* entry point for linux usb interface
unsigned int *rate_table; /* rate table */
unsigned char clock; /* associated clock */
struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
+ bool dsd_dop; /* add DOP headers in case of DSD samples */
+ bool dsd_bitrev; /* reverse the bits of each DSD sample */
};
struct snd_usb_substream;
unsigned int altset_idx; /* USB data format: index of alternate setting */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
unsigned int fmt_type; /* USB audio format type (1-3) */
+ unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
unsigned int running: 1; /* running status */
int last_frame_number; /* stored frame number */
int last_delay; /* stored delay */
+
+ struct {
+ int marker;
+ int channel;
+ int byte_idx;
+ } dsd_dop;
};
struct snd_usb_stream {
#include "card.h"
#include "helper.h"
#include "clock.h"
+#include "quirks.h"
static struct uac_clock_source_descriptor *
snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
return buf;
}
+static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_id,
+ unsigned char pin)
+{
+ int ret;
+
+ ret = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+ UAC2_CS_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+ UAC2_CX_CLOCK_SELECTOR << 8,
+ snd_usb_ctrl_intf(chip) | (selector_id << 8),
+ &pin, sizeof(pin));
+ if (ret < 0)
+ return ret;
+
+ if (ret != sizeof(pin)) {
+ snd_printk(KERN_ERR
+ "usb-audio:%d: setting selector (id %d) unexpected length %d\n",
+ chip->dev->devnum, selector_id, ret);
+ return -EINVAL;
+ }
+
+ ret = uac_clock_selector_get_val(chip, selector_id);
+ if (ret < 0)
+ return ret;
+
+ if (ret != pin) {
+ snd_printk(KERN_ERR
+ "usb-audio:%d: setting selector (id %d) to %x failed (current: %d)\n",
+ chip->dev->devnum, selector_id, pin, ret);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
{
int err;
}
static int __uac_clock_find_source(struct snd_usb_audio *chip,
- int entity_id, unsigned long *visited)
+ int entity_id, unsigned long *visited,
+ bool validate)
{
struct uac_clock_source_descriptor *source;
struct uac_clock_selector_descriptor *selector;
/* first, see if the ID we're looking for is a clock source already */
source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
- if (source)
- return source->bClockID;
+ if (source) {
+ entity_id = source->bClockID;
+ if (validate && !uac_clock_source_is_valid(chip, entity_id)) {
+ snd_printk(KERN_ERR "usb-audio:%d: clock source %d is not valid, cannot use\n",
+ chip->dev->devnum, entity_id);
+ return -ENXIO;
+ }
+ return entity_id;
+ }
selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
if (selector) {
- int ret;
+ int ret, i, cur;
/* the entity ID we are looking for is a selector.
* find out what it currently selects */
/* Selector values are one-based */
if (ret > selector->bNrInPins || ret < 1) {
- printk(KERN_ERR
+ snd_printk(KERN_ERR
"%s(): selector reported illegal value, id %d, ret %d\n",
__func__, selector->bClockID, ret);
return -EINVAL;
}
- return __uac_clock_find_source(chip, selector->baCSourceID[ret-1],
- visited);
+ cur = ret;
+ ret = __uac_clock_find_source(chip, selector->baCSourceID[ret - 1],
+ visited, validate);
+ if (!validate || ret > 0 || !chip->autoclock)
+ return ret;
+
+ /* The current clock source is invalid, try others. */
+ for (i = 1; i <= selector->bNrInPins; i++) {
+ int err;
+
+ if (i == cur)
+ continue;
+
+ ret = __uac_clock_find_source(chip, selector->baCSourceID[i - 1],
+ visited, true);
+ if (ret < 0)
+ continue;
+
+ err = uac_clock_selector_set_val(chip, entity_id, i);
+ if (err < 0)
+ continue;
+
+ snd_printk(KERN_INFO
+ "usb-audio:%d: found and selected valid clock source %d\n",
+ chip->dev->devnum, ret);
+ return ret;
+ }
+
+ return -ENXIO;
}
/* FIXME: multipliers only act as pass-thru element for now */
multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
if (multiplier)
return __uac_clock_find_source(chip, multiplier->bCSourceID,
- visited);
+ visited, validate);
return -EINVAL;
}
*
* Returns the clock source UnitID (>=0) on success, or an error.
*/
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id)
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ bool validate)
{
DECLARE_BITMAP(visited, 256);
memset(visited, 0, sizeof(visited));
- return __uac_clock_find_source(chip, entity_id, visited);
+ return __uac_clock_find_source(chip, entity_id, visited, validate);
}
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
return 0;
}
-static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
- struct usb_host_interface *alts,
- struct audioformat *fmt, int rate)
+static int get_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+ int altsetting, int clock)
{
struct usb_device *dev = chip->dev;
- unsigned char data[4];
- int err, cur_rate, prev_rate;
- int clock = snd_usb_clock_find_source(chip, fmt->clock);
-
- if (clock < 0)
- return clock;
-
- if (!uac_clock_source_is_valid(chip, clock)) {
- /* TODO: should we try to find valid clock setups by ourself? */
- snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
- dev->devnum, iface, fmt->altsetting, clock);
- return -ENXIO;
- }
+ __le32 data;
+ int err;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
snd_usb_ctrl_intf(chip) | (clock << 8),
- data, sizeof(data));
+ &data, sizeof(data));
if (err < 0) {
- snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
- dev->devnum, iface, fmt->altsetting);
- prev_rate = 0;
- } else {
- prev_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2): err %d\n",
+ dev->devnum, iface, altsetting, err);
+ return 0;
}
- data[0] = rate;
- data[1] = rate >> 8;
- data[2] = rate >> 16;
- data[3] = rate >> 24;
- if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
- data, sizeof(data))) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
- dev->devnum, iface, fmt->altsetting, rate);
- return err;
- }
+ return le32_to_cpu(data);
+}
- err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
- UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
- data, sizeof(data));
- if (err < 0) {
- snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
- dev->devnum, iface, fmt->altsetting);
- cur_rate = 0;
+static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+ struct usb_host_interface *alts,
+ struct audioformat *fmt, int rate)
+{
+ struct usb_device *dev = chip->dev;
+ __le32 data;
+ int err, cur_rate, prev_rate;
+ int clock;
+ bool writeable;
+ struct uac_clock_source_descriptor *cs_desc;
+
+ clock = snd_usb_clock_find_source(chip, fmt->clock, true);
+ if (clock < 0)
+ return clock;
+
+ prev_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
+
+ cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock);
+ writeable = uac2_control_is_writeable(cs_desc->bmControls, UAC2_CS_CONTROL_SAM_FREQ - 1);
+ if (writeable) {
+ data = cpu_to_le32(rate);
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ UAC2_CS_CONTROL_SAM_FREQ << 8,
+ snd_usb_ctrl_intf(chip) | (clock << 8),
+ &data, sizeof(data));
+ if (err < 0) {
+ snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2): err %d\n",
+ dev->devnum, iface, fmt->altsetting, rate, err);
+ return err;
+ }
+
+ cur_rate = get_sample_rate_v2(chip, iface, fmt->altsetting, clock);
} else {
- cur_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ cur_rate = prev_rate;
}
if (cur_rate != rate) {
+ if (!writeable) {
+ snd_printk(KERN_WARNING
+ "%d:%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n",
+ dev->devnum, iface, fmt->altsetting, rate, cur_rate);
+ return -ENXIO;
+ }
snd_printd(KERN_WARNING
"current rate %d is different from the runtime rate %d\n",
cur_rate, rate);
* interface is active. */
if (rate != prev_rate) {
usb_set_interface(dev, iface, 0);
+ snd_usb_set_interface_quirk(dev);
usb_set_interface(dev, iface, fmt->altsetting);
+ snd_usb_set_interface_quirk(dev);
}
return 0;
struct usb_host_interface *alts,
struct audioformat *fmt, int rate);
-int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id,
+ bool validate);
#endif /* __USBAUDIO_CLOCK_H */
* Determine whether an endpoint is driven by an implicit feedback
* data endpoint source.
*/
-int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
{
return ep->sync_master &&
ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
goto exit_clear;
- if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+ if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
unsigned long flags;
spin_lock_irqsave(&ep->lock, flags);
struct usb_host_interface *alts,
int ep_num, int direction, int type)
{
- struct list_head *p;
struct snd_usb_endpoint *ep;
int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
mutex_lock(&chip->mutex);
- list_for_each(p, &chip->ep_list) {
- ep = list_entry(p, struct snd_usb_endpoint, list);
+ list_for_each_entry(ep, &chip->ep_list, list) {
if (ep->ep_num == ep_num &&
ep->iface == alts->desc.bInterfaceNumber &&
ep->alt_idx == alts->desc.bAlternateSetting) {
int is_playback = usb_pipeout(ep->pipe);
int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
+ /*
+ * When operating in DSD DOP mode, the size of a sample frame
+ * in hardware differs from the actual physical format width
+ * because we need to make room for the DOP markers.
+ */
+ frame_bits += channels << 3;
+ }
+
ep->datainterval = fmt->datainterval;
ep->stride = frame_bits >> 3;
ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
else
packs_per_ms = 1;
- if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+ if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
urb_packs = max(ep->chip->nrpacks, 1);
urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
} else {
urb_packs *= packs_per_ms;
- if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+ if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep))
urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
/* decide how many packets to be used */
- if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+ if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) {
unsigned int minsize, maxpacks;
/* determine how small a packet can be */
minsize = (ep->freqn >> (16 - ep->datainterval))
set_bit(EP_FLAG_RUNNING, &ep->flags);
- if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+ if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
for (i = 0; i < ep->nurbs; i++) {
struct snd_urb_ctx *ctx = ep->urb + i;
list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
* and add it to the list of pending urbs. queue_pending_output_urbs()
* will take care of them later.
*/
- if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+ if (snd_usb_endpoint_implicit_feedback_sink(ep) &&
ep->use_count != 0) {
/* implicit feedback case */
int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free(struct list_head *head);
-int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep);
void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
*/
static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp,
- int format, void *_fmt,
+ unsigned int format, void *_fmt,
int protocol)
{
int sample_width, sample_bytes;
- u64 pcm_formats;
+ u64 pcm_formats = 0;
switch (protocol) {
case UAC_VERSION_1:
struct uac_format_type_i_ext_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubslotSize;
+
+ if (format & UAC2_FORMAT_TYPE_I_RAW_DATA)
+ pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+
format <<= 1;
break;
}
}
- pcm_formats = 0;
-
- if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
+ if ((pcm_formats == 0) &&
+ (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
/* some devices don't define this correctly... */
snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
chip->dev->devnum, fp->iface, fp->altsetting);
snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
chip->dev->devnum, fp->iface, fp->altsetting, format);
}
+
+ pcm_formats |= snd_usb_interface_dsd_format_quirks(chip, fp, sample_bytes);
+
return pcm_formats;
}
struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0;
- int clock = snd_usb_clock_find_source(chip, fp->clock);
+ int clock = snd_usb_clock_find_source(chip, fp->clock, false);
if (clock < 0) {
snd_printk(KERN_ERR "%s(): unable to find clock source (clock %d)\n",
* parse the format type I and III descriptors
*/
static int parse_audio_format_i(struct snd_usb_audio *chip,
- struct audioformat *fp, int format,
+ struct audioformat *fp, unsigned int format,
struct uac_format_type_i_continuous_descriptor *fmt,
struct usb_host_interface *iface)
{
return ret;
}
-int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
- int format, struct uac_format_type_i_continuous_descriptor *fmt,
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
+ struct audioformat *fp, unsigned int format,
+ struct uac_format_type_i_continuous_descriptor *fmt,
int stream, struct usb_host_interface *iface)
{
int err;
#define __USBAUDIO_FORMAT_H
int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
- struct audioformat *fp, int format,
+ struct audioformat *fp, unsigned int format,
struct uac_format_type_i_continuous_descriptor *fmt,
int stream, struct usb_host_interface *iface);
struct snd_usb_midi_in_endpoint *in;
} endpoints[MIDI_MAX_ENDPOINTS];
unsigned long input_triggered;
- bool autopm_reference;
unsigned int opened[2];
unsigned char disconnected;
unsigned char input_running;
{
struct snd_usb_midi* umidi = substream->rmidi->private_data;
struct snd_kcontrol *ctl;
- int err;
down_read(&umidi->disc_rwsem);
if (umidi->disconnected) {
mutex_lock(&umidi->mutex);
if (open) {
if (!umidi->opened[0] && !umidi->opened[1]) {
- err = usb_autopm_get_interface(umidi->iface);
- umidi->autopm_reference = err >= 0;
- if (err < 0 && err != -EACCES) {
- mutex_unlock(&umidi->mutex);
- up_read(&umidi->disc_rwsem);
- return -EIO;
- }
if (umidi->roland_load_ctl) {
ctl = umidi->roland_load_ctl;
ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(umidi->card,
SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
}
- if (umidi->autopm_reference)
- usb_autopm_put_interface(umidi->iface);
}
}
mutex_unlock(&umidi->mutex);
}
del_timer_sync(&umidi->error_timer);
}
+EXPORT_SYMBOL(snd_usbmidi_disconnect);
static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
{
static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi,
int stream, int number)
{
- struct list_head* list;
+ struct snd_rawmidi_substream *substream;
- list_for_each(list, &umidi->rmidi->streams[stream].substreams) {
- struct snd_rawmidi_substream *substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream, &umidi->rmidi->streams[stream].substreams, list) {
if (substream->number == number)
return substream;
}
}
umidi->input_running = 0;
}
+EXPORT_SYMBOL(snd_usbmidi_input_stop);
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
{
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
umidi->input_running = 1;
}
+EXPORT_SYMBOL(snd_usbmidi_input_start);
/*
* Creates and registers everything needed for a MIDI streaming interface.
return err;
}
+ usb_autopm_get_interface_no_resume(umidi->iface);
+
list_add_tail(&umidi->list, midi_list);
return 0;
}
-
EXPORT_SYMBOL(snd_usbmidi_create);
-EXPORT_SYMBOL(snd_usbmidi_input_stop);
-EXPORT_SYMBOL(snd_usbmidi_input_start);
-EXPORT_SYMBOL(snd_usbmidi_disconnect);
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/bitrev.h>
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
*/
static struct audioformat *find_format(struct snd_usb_substream *subs)
{
- struct list_head *p;
+ struct audioformat *fp;
struct audioformat *found = NULL;
int cur_attr = 0, attr;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!(fp->formats & (1uLL << subs->pcm_format)))
continue;
if (fp->channels != subs->channels)
fmt->iface, fmt->altsetting);
subs->interface = fmt->iface;
subs->altset_idx = fmt->altset_idx;
+
+ snd_usb_set_interface_quirk(dev);
}
subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax;
int changed;
hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
unsigned int rmin, rmax;
int changed;
hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
changed = 0;
rmin = rmax = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
- struct list_head *p;
+ struct audioformat *fp;
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
u64 fbits;
u32 oldbits[2];
hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
fbits = 0;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
fbits |= fp->formats;
static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{
- struct list_head *p;
+ struct audioformat *fp;
unsigned int pt, ptmin;
int param_period_time_if_needed;
int err;
runtime->hw.rates = 0;
ptmin = UINT_MAX;
/* check min/max rates and channels */
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry(fp, &subs->fmt_list, list) {
runtime->hw.rates |= fp->rates;
if (runtime->hw.rate_min > fp->rate_min)
runtime->hw.rate_min = fp->rate_min;
runtime->private_data = subs;
subs->pcm_substream = substream;
/* runtime PM is also done there */
+
+ /* initialize DSD/DOP context */
+ subs->dsd_dop.byte_idx = 0;
+ subs->dsd_dop.channel = 0;
+ subs->dsd_dop.marker = 1;
+
return setup_hw_info(runtime, subs);
}
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
- cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj;
if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
snd_printdd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
// continue;
snd_pcm_period_elapsed(subs->pcm_substream);
}
+static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
+ struct urb *urb, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ unsigned int stride = runtime->frame_bits >> 3;
+ unsigned int dst_idx = 0;
+ unsigned int src_idx = subs->hwptr_done;
+ unsigned int wrap = runtime->buffer_size * stride;
+ u8 *dst = urb->transfer_buffer;
+ u8 *src = runtime->dma_area;
+ u8 marker[] = { 0x05, 0xfa };
+
+ /*
+ * The DSP DOP format defines a way to transport DSD samples over
+ * normal PCM data endpoints. It requires stuffing of marker bytes
+ * (0x05 and 0xfa, alternating per sample frame), and then expects
+ * 2 additional bytes of actual payload. The whole frame is stored
+ * LSB.
+ *
+ * Hence, for a stereo transport, the buffer layout looks like this,
+ * where L refers to left channel samples and R to right.
+ *
+ * L1 L2 0x05 R1 R2 0x05 L3 L4 0xfa R3 R4 0xfa
+ * L5 L6 0x05 R5 R6 0x05 L7 L8 0xfa R7 R8 0xfa
+ * .....
+ *
+ */
+
+ while (bytes--) {
+ if (++subs->dsd_dop.byte_idx == 3) {
+ /* frame boundary? */
+ dst[dst_idx++] = marker[subs->dsd_dop.marker];
+ src_idx += 2;
+ subs->dsd_dop.byte_idx = 0;
+
+ if (++subs->dsd_dop.channel % runtime->channels == 0) {
+ /* alternate the marker */
+ subs->dsd_dop.marker++;
+ subs->dsd_dop.marker %= ARRAY_SIZE(marker);
+ subs->dsd_dop.channel = 0;
+ }
+ } else {
+ /* stuff the DSD payload */
+ int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap;
+
+ if (subs->cur_audiofmt->dsd_bitrev)
+ dst[dst_idx++] = bitrev8(src[idx]);
+ else
+ dst[dst_idx++] = src[idx];
+
+ subs->hwptr_done++;
+ }
+ }
+}
+
static void prepare_playback_urb(struct snd_usb_substream *subs,
struct urb *urb)
{
counts = snd_usb_endpoint_next_packet_size(ep);
/* set up descriptor */
- urb->iso_frame_desc[i].offset = frames * stride;
- urb->iso_frame_desc[i].length = counts * stride;
+ urb->iso_frame_desc[i].offset = frames * ep->stride;
+ urb->iso_frame_desc[i].length = counts * ep->stride;
frames += counts;
urb->number_of_packets++;
subs->transfer_done += counts;
frames -= subs->transfer_done;
counts -= subs->transfer_done;
urb->iso_frame_desc[i].length =
- counts * stride;
+ counts * ep->stride;
subs->transfer_done = 0;
}
i++;
if (i < ctx->packets) {
/* add a transfer delimiter */
urb->iso_frame_desc[i].offset =
- frames * stride;
+ frames * ep->stride;
urb->iso_frame_desc[i].length = 0;
urb->number_of_packets++;
}
}
}
if (period_elapsed &&
- !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+ !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
break;
}
- bytes = frames * stride;
- if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
- /* err, the transferred area goes over buffer boundary. */
- unsigned int bytes1 =
- runtime->buffer_size * stride - subs->hwptr_done;
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes1);
- memcpy(urb->transfer_buffer + bytes1,
- runtime->dma_area, bytes - bytes1);
+ bytes = frames * ep->stride;
+
+ if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
+ subs->cur_audiofmt->dsd_dop)) {
+ fill_playback_urb_dsd_dop(subs, urb, bytes);
+ } else if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U8 &&
+ subs->cur_audiofmt->dsd_bitrev)) {
+ /* bit-reverse the bytes */
+ u8 *buf = urb->transfer_buffer;
+ for (i = 0; i < bytes; i++) {
+ int idx = (subs->hwptr_done + i)
+ % (runtime->buffer_size * stride);
+ buf[i] = bitrev8(runtime->dma_area[idx]);
+ }
+
+ subs->hwptr_done += bytes;
} else {
- memcpy(urb->transfer_buffer,
- runtime->dma_area + subs->hwptr_done, bytes);
+ /* usual PCM */
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+
+ subs->hwptr_done += bytes;
}
- subs->hwptr_done += bytes;
+
if (subs->hwptr_done >= runtime->buffer_size * stride)
subs->hwptr_done -= runtime->buffer_size * stride;
{
unsigned long flags;
struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
- int stride = runtime->frame_bits >> 3;
- int processed = urb->transfer_buffer_length / stride;
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
+ int processed = urb->transfer_buffer_length / ep->stride;
int est_delay;
/* ignore the delay accounting when procssed=0 is given, i.e.
*/
static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
{
- struct list_head *p;
+ struct audioformat *fp;
static char *sync_types[4] = {
"NONE", "ASYNC", "ADAPTIVE", "SYNC"
};
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
snd_pcm_format_t fmt;
- fp = list_entry(p, struct audioformat, list);
+
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
snd_iprintf(buffer, " Format:");
.type = QUIRK_MIDI_RAW_BYTES
}
},
+{
+ USB_DEVICE(0x1235, 0x0018),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Novation",
+ .product_name = "Twitch",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .channels = 4,
+ .iface = 0,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .nr_rates = 2,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000
+ }
+ }
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_MIDI_RAW_BYTES
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
{
USB_DEVICE_VENDOR_SPEC(0x1235, 0x4661),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
.endpoint = 0x02,
.ep_attr = 0x01,
- .maxpacksize = 0x130,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.rate_min = 44100,
.attributes = 0x00,
.endpoint = 0x03,
.ep_attr = USB_ENDPOINT_SYNC_ASYNC,
- .maxpacksize = 0x128,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
.endpoint = 0x85,
.ep_attr = USB_ENDPOINT_SYNC_SYNC,
- .maxpacksize = 0x128,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
return -EINVAL;
}
alts = &iface->altsetting[fp->altset_idx];
- fp->datainterval = snd_usb_parse_datainterval(chip, alts);
- fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ if (fp->datainterval == 0)
+ fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+ if (fp->maxpacksize == 0)
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
usb_set_interface(chip->dev, fp->iface, 0);
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return err;
}
+/*
+ * Novation Twitch DJ controller
+ */
+static int snd_usb_twitch_boot_quirk(struct usb_device *dev)
+{
+ /* preemptively set up the device because otherwise the
+ * raw MIDI endpoints are not active */
+ usb_set_interface(dev, 0, 1);
+ return 0;
+}
+
/*
* This call will put the synth in "USB send" mode, i.e it will send MIDI
* messages through USB (this is disabled at startup). The synth will
/* Digidesign Mbox 2 */
return snd_usb_mbox2_boot_quirk(dev);
+ case USB_ID(0x1235, 0x0018):
+ /* Focusrite Novation Twitch */
+ return snd_usb_twitch_boot_quirk(dev);
+
case USB_ID(0x133e, 0x0815):
/* Access Music VirusTI Desktop */
return snd_usb_accessmusic_boot_quirk(dev);
break;
}
snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
+ subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
}
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
ep->skip_packets = 16;
}
+void snd_usb_set_interface_quirk(struct usb_device *dev)
+{
+ /*
+ * "Playback Design" products need a 50ms delay after setting the
+ * USB interface.
+ */
+ if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba)
+ mdelay(50);
+}
+
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)
mdelay(20);
}
+/*
+ * snd_usb_interface_dsd_format_quirks() is called from format.c to
+ * augment the PCM format bit-field for DSD types. The UAC standards
+ * don't have a designated bit field to denote DSD-capable interfaces,
+ * hence all hardware that is known to support this format has to be
+ * listed here.
+ */
+u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ unsigned int sample_bytes)
+{
+ /* Playback Designs */
+ if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+ switch (fp->altsetting) {
+ case 1:
+ fp->dsd_dop = true;
+ return SNDRV_PCM_FMTBIT_DSD_U16_LE;
+ case 2:
+ fp->dsd_bitrev = true;
+ return SNDRV_PCM_FMTBIT_DSD_U8;
+ case 3:
+ fp->dsd_bitrev = true;
+ return SNDRV_PCM_FMTBIT_DSD_U16_LE;
+ }
+ }
+
+ return 0;
+}
void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep);
+void snd_usb_set_interface_quirk(struct usb_device *dev);
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);
+u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
+ struct audioformat *fp,
+ unsigned int sample_bytes);
+
#endif /* __USBAUDIO_QUIRKS_H */
*/
static void free_substream(struct snd_usb_substream *subs)
{
- struct list_head *p, *n;
+ struct audioformat *fp, *n;
if (!subs->num_formats)
return; /* not initialized */
- list_for_each_safe(p, n, &subs->fmt_list) {
- struct audioformat *fp = list_entry(p, struct audioformat, list);
+ list_for_each_entry_safe(fp, n, &subs->fmt_list, list) {
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
subs->speed = snd_usb_get_speed(subs->dev);
+ subs->pkt_offset_adj = 0;
snd_usb_set_pcm_ops(as->pcm, stream);
int stream,
struct audioformat *fp)
{
- struct list_head *p;
struct snd_usb_stream *as;
struct snd_usb_substream *subs;
struct snd_pcm *pcm;
int err;
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
+ list_for_each_entry(as, &chip->pcm_list, list) {
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
}
}
/* look for an empty stream */
- list_for_each(p, &chip->pcm_list) {
- as = list_entry(p, struct snd_usb_stream, list);
+ list_for_each_entry(as, &chip->pcm_list, list) {
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
int i, altno, err, stream;
- int format = 0, num_channels = 0;
+ unsigned int format = 0, num_channels = 0;
struct audioformat *fp = NULL;
int num, protocol, clock = 0;
struct uac_format_type_i_continuous_descriptor *fmt;
int setup; /* from the 'device_setup' module param */
int nrpacks; /* from the 'nrpacks' module param */
+ bool autoclock; /* from the 'autoclock' module param */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
};