Merge tag 'asoc-v3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v3.16
Lots of cleanup work going on in the core this release but very little
visible to external users except for the new drivers that have been
added.
- Support for specifying aux CODECs in DT.
- Removal of the deprecated mux and enum macros.
- More moves towards full componentisation.
- Removal of some unused I/O code.
- Lots of cleanups, fixes and enhancements to the davinci, Freescale,
Haswell and Realtek drivers.
- Several drivers exposed directly in Kconfig for use with simple-card.
- New drivers for Cirrus CS42L56, Realtek RT5639, RT5642 and RT5651 and
ST STA350.
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt
new file mode 100644
index 0000000..c26f8ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt
@@ -0,0 +1,28 @@
+NVIDIA Tegra30 HDA controller
+
+Required properties:
+- compatible : "nvidia,tegra30-hda"
+- reg : Should contain the HDA registers location and length.
+- interrupts : The interrupt from the HDA controller.
+- clocks : Must contain an entry for each required entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
+- resets : Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
+
+Example:
+
+hda@0,70030000 {
+ compatible = "nvidia,tegra124-hda", "nvidia,tegra30-hda";
+ reg = <0x0 0x70030000 0x0 0x10000>;
+ interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&tegra_car TEGRA124_CLK_HDA>,
+ <&tegra_car TEGRA124_CLK_HDA2HDMI>,
+ <&tegra_car TEGRA124_CLK_HDA2CODEC_2X>;
+ clock-names = "hda", "hda2hdmi", "hdacodec_2x";
+ resets = <&tegra_car 125>, /* hda */
+ <&tegra_car 128>; /* hda2hdmi */
+ <&tegra_car 111>, /* hda2codec_2x */
+ reset-names = "hda", "hda2hdmi", "hdacodec_2x";
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 3be14ee..2c4cc22 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8247,6 +8247,7 @@
W: http://www.alsa-project.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
T: git git://git.alsa-project.org/alsa-kernel.git
+Q: http://patchwork.kernel.org/project/alsa-devel/list/
S: Maintained
F: Documentation/sound/
F: include/sound/
diff --git a/include/sound/atmel-ac97c.h b/include/sound/atmel-ac97c.h
index e6aabdb..00e6c289 100644
--- a/include/sound/atmel-ac97c.h
+++ b/include/sound/atmel-ac97c.h
@@ -23,7 +23,6 @@
* @reset_pin: GPIO pin wired to the reset input on the external AC97 codec,
* optional to use, set to -ENODEV if not in use. AC97 layer will
* try to do a software reset of the external codec anyway.
- * @flags: Flags for which directions should be enabled.
*
* If the user do not want to use a DMA channel for playback or capture, i.e.
* only one feature is required on the board. The slave for playback or capture
@@ -33,7 +32,6 @@
struct ac97c_platform_data {
struct dw_dma_slave rx_dws;
struct dw_dma_slave tx_dws;
- unsigned int flags;
int reset_pin;
};
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 05ec049..a04d2317 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -1198,6 +1198,7 @@
}
static struct platform_driver atmel_ac97c_driver = {
+ .probe = atmel_ac97c_probe,
.remove = atmel_ac97c_remove,
.driver = {
.name = "atmel_ac97c",
@@ -1205,19 +1206,7 @@
.pm = ATMEL_AC97C_PM_OPS,
},
};
-
-static int __init atmel_ac97c_init(void)
-{
- return platform_driver_probe(&atmel_ac97c_driver,
- atmel_ac97c_probe);
-}
-module_init(atmel_ac97c_init);
-
-static void __exit atmel_ac97c_exit(void)
-{
- platform_driver_unregister(&atmel_ac97c_driver);
-}
-module_exit(atmel_ac97c_exit);
+module_platform_driver(atmel_ac97c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index ce83def..9acc77e 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -345,7 +345,7 @@
snd_pcm_debug_name(substream, name, sizeof(name));
xrun_log_show(substream);
pcm_err(substream->pcm,
- "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 3e05c55..a1fd77a 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -362,13 +362,13 @@
if (! port->name[0]) {
if (info->name[0]) {
if (ports > 1)
- snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
+ snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
else
snprintf(port->name, sizeof(port->name), "%s", info->name);
} else {
/* last resort */
if (ports > 1)
- sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+ sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
else
sprintf(port->name, "MIDI %d-%d", card->number, device);
}
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index 25e4609..3bbc3ec 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -567,7 +567,6 @@
static int mpu401_command(int dev, mpu_command_rec * cmd)
{
int i, timeout, ok;
- int ret = 0;
unsigned long flags;
struct mpu_config *devc;
@@ -644,7 +643,6 @@
}
}
}
- ret = 0;
cmd->data[0] = 0;
if (cmd->nr_returns)
@@ -666,7 +664,7 @@
}
}
spin_unlock_irqrestore(&devc->lock,flags);
- return ret;
+ return 0;
}
static int mpu_cmd(int dev, int cmd, int data)
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index db18cca..529f5f4 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -34,8 +35,6 @@
#include <sound/opl3.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <media/tea575x.h>
#endif
@@ -80,7 +79,10 @@
* Direct registers
*/
-#define FM801_REG(chip, reg) (chip->port + FM801_##reg)
+#define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg)
+#define fm801_readw(chip,reg) inw(chip->port + FM801_##reg)
+
+#define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg)
#define FM801_PCM_VOL 0x00 /* PCM Output Volume */
#define FM801_FM_VOL 0x02 /* FM Output Volume */
@@ -156,21 +158,27 @@
#define FM801_GPIO_GS3 (1<<15)
#define FM801_GPIO_GS(x) (1<<(12+(x)))
-/*
-
+/**
+ * struct fm801 - describes FM801 chip
+ * @port: I/O port number
+ * @multichannel: multichannel support
+ * @secondary: secondary codec
+ * @secondary_addr: address of the secondary codec
+ * @tea575x_tuner: tuner access method & flags
+ * @ply_ctrl: playback control
+ * @cap_ctrl: capture control
*/
-
struct fm801 {
int irq;
- unsigned long port; /* I/O port number */
- unsigned int multichannel: 1, /* multichannel support */
- secondary: 1; /* secondary codec */
- unsigned char secondary_addr; /* address of the secondary codec */
- unsigned int tea575x_tuner; /* tuner access method & flags */
+ unsigned long port;
+ unsigned int multichannel: 1,
+ secondary: 1;
+ unsigned char secondary_addr;
+ unsigned int tea575x_tuner;
- unsigned short ply_ctrl; /* playback control */
- unsigned short cap_ctrl; /* capture control */
+ unsigned short ply_ctrl;
+ unsigned short cap_ctrl;
unsigned long ply_buffer;
unsigned int ply_buf;
@@ -222,6 +230,30 @@
* common I/O routines
*/
+static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
+static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short mask, unsigned short value)
{
@@ -244,73 +276,54 @@
unsigned short val)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return;
}
- dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
- return;
- ok1:
/* write data and address */
- outw(val, FM801_REG(chip, AC97_DATA));
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_DATA, val);
+ fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
/*
* Wait until the write command is not completed..
- */
- for (idx = 0; idx < 1000; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- return;
- udelay(10);
- }
- dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
+ */
+ if (!fm801_ac97_is_ready(chip, 1000))
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
}
static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return 0;
}
- dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
- return 0;
- ok1:
/* read command */
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
- FM801_REG(chip, AC97_CMD));
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok2;
- udelay(10);
+ fm801_writew(chip, AC97_CMD,
+ reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
+ return 0;
}
- dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
- return 0;
- ok2:
- for (idx = 0; idx < 1000; idx++) {
- if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
- goto ok3;
- udelay(10);
+ if (!fm801_ac97_is_valid(chip, 1000)) {
+ dev_err(chip->card->dev,
+ "AC'97 interface #%d is not valid (2)\n", ac97->num);
+ return 0;
}
- dev_err(chip->card->dev, "AC'97 interface #%d is not valid (2)\n", ac97->num);
- return 0;
- ok3:
- return inw(FM801_REG(chip, AC97_DATA));
+ return fm801_readw(chip, AC97_DATA);
}
static unsigned int rates[] = {
@@ -384,7 +397,7 @@
snd_BUG();
return -EINVAL;
}
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
spin_unlock(&chip->reg_lock);
return 0;
}
@@ -419,7 +432,7 @@
snd_BUG();
return -EINVAL;
}
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
spin_unlock(&chip->reg_lock);
return 0;
}
@@ -457,12 +470,13 @@
}
chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->ply_buf = 0;
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
- outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
+ fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
chip->ply_buffer = runtime->dma_addr;
chip->ply_pos = 0;
- outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
- outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
+ fm801_writel(chip, PLY_BUF2,
+ chip->ply_buffer + (chip->ply_count % chip->ply_size));
spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -483,12 +497,13 @@
chip->cap_ctrl |= FM801_STEREO;
chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->cap_buf = 0;
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
- outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
+ fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
chip->cap_buffer = runtime->dma_addr;
chip->cap_pos = 0;
- outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
- outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
+ fm801_writel(chip, CAP_BUF2,
+ chip->cap_buffer + (chip->cap_count % chip->cap_size));
spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -501,8 +516,8 @@
if (!(chip->ply_ctrl & FM801_START))
return 0;
spin_lock(&chip->reg_lock);
- ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+ ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
ptr += chip->ply_count;
ptr %= chip->ply_size;
}
@@ -518,8 +533,8 @@
if (!(chip->cap_ctrl & FM801_START))
return 0;
spin_lock(&chip->reg_lock);
- ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+ ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
ptr += chip->cap_count;
ptr %= chip->cap_size;
}
@@ -533,12 +548,12 @@
unsigned short status;
unsigned int tmp;
- status = inw(FM801_REG(chip, IRQ_STATUS));
+ status = fm801_readw(chip, IRQ_STATUS);
status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
if (! status)
return IRQ_NONE;
/* ack first */
- outw(status, FM801_REG(chip, IRQ_STATUS));
+ fm801_writew(chip, IRQ_STATUS, status);
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
spin_lock(&chip->reg_lock);
chip->ply_buf++;
@@ -546,10 +561,10 @@
chip->ply_pos %= chip->ply_size;
tmp = chip->ply_pos + chip->ply_count;
tmp %= chip->ply_size;
- outl(chip->ply_buffer + tmp,
- (chip->ply_buf & 1) ?
- FM801_REG(chip, PLY_BUF1) :
- FM801_REG(chip, PLY_BUF2));
+ if (chip->ply_buf & 1)
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
+ else
+ fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->playback_substream);
}
@@ -560,10 +575,10 @@
chip->cap_pos %= chip->cap_size;
tmp = chip->cap_pos + chip->cap_count;
tmp %= chip->cap_size;
- outl(chip->cap_buffer + tmp,
- (chip->cap_buf & 1) ?
- FM801_REG(chip, CAP_BUF1) :
- FM801_REG(chip, CAP_BUF2));
+ if (chip->cap_buf & 1)
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
+ else
+ fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(chip->capture_substream);
}
@@ -747,7 +762,7 @@
static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
reg &= ~(FM801_GPIO_GP(gpio.data) |
@@ -759,13 +774,13 @@
/* WRITE_ENABLE is inverted */
reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
+ fm801_writew(chip, GPIO_CTRL, reg);
}
static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
u8 ret;
@@ -780,7 +795,7 @@
static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
/* use GPIO lines and set write enable bit */
@@ -811,7 +826,7 @@
FM801_GPIO_GP(gpio.clk));
}
- outw(reg, FM801_REG(chip, GPIO_CTRL));
+ fm801_writew(chip, GPIO_CTRL, reg);
}
static struct snd_tea575x_ops snd_fm801_tea_ops = {
@@ -962,7 +977,7 @@
struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val;
- val = inw(FM801_REG(chip, REC_SRC)) & 7;
+ val = fm801_readw(chip, REC_SRC) & 7;
if (val > 4)
val = 4;
ucontrol->value.enumerated.item[0] = val;
@@ -1073,12 +1088,12 @@
{
unsigned long timeout = jiffies + waits;
- outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
- FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_CMD,
+ reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
udelay(5);
do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
- == FM801_AC97_VALID)
+ if ((fm801_readw(chip, AC97_CMD) &
+ (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_after(timeout, jiffies));
@@ -1093,10 +1108,10 @@
goto __ac97_ok;
/* codec cold reset + AC'97 warm reset */
- outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
- inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+ fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
+ fm801_readw(chip, CODEC_CTRL); /* flush posting data */
udelay(100);
- outw(0, FM801_REG(chip, CODEC_CTRL));
+ fm801_writew(chip, CODEC_CTRL, 0);
if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
if (!resume) {
@@ -1117,7 +1132,7 @@
for (i = 3; i > 0; i--) {
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
msecs_to_jiffies(50))) {
- cmdw = inw(FM801_REG(chip, AC97_DATA));
+ cmdw = fm801_readw(chip, AC97_DATA);
if (cmdw != 0xffff && cmdw != 0) {
chip->secondary = 1;
chip->secondary_addr = i;
@@ -1135,23 +1150,24 @@
__ac97_ok:
/* init volume */
- outw(0x0808, FM801_REG(chip, PCM_VOL));
- outw(0x9f1f, FM801_REG(chip, FM_VOL));
- outw(0x8808, FM801_REG(chip, I2S_VOL));
+ fm801_writew(chip, PCM_VOL, 0x0808);
+ fm801_writew(chip, FM_VOL, 0x9f1f);
+ fm801_writew(chip, I2S_VOL, 0x8808);
/* I2S control - I2S mode */
- outw(0x0003, FM801_REG(chip, I2S_MODE));
+ fm801_writew(chip, I2S_MODE, 0x0003);
/* interrupt setup */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
if (chip->irq < 0)
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
else
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
/* interrupt clear */
- outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+ fm801_writew(chip, IRQ_STATUS,
+ FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
return 0;
}
@@ -1165,9 +1181,9 @@
goto __end_hw;
/* interrupt setup - mask everything */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
cmdw |= 0x00c3;
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
__end_hw:
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
@@ -1339,15 +1355,15 @@
return err;
}
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
- FM801_REG(chip, MPU401_DATA),
+ chip->port + FM801_MPU401_DATA,
MPU401_INFO_INTEGRATED |
MPU401_INFO_IRQ_HOOK,
-1, &chip->rmidi)) < 0) {
snd_card_free(card);
return err;
}
- if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
- FM801_REG(chip, OPL3_BANK1),
+ if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
+ chip->port + FM801_OPL3_BANK1,
OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index ac17c3f..ebf4c2f 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -20,6 +20,21 @@
To compile this driver as a module, choose M here: the module
will be called snd-hda-intel.
+config SND_HDA_TEGRA
+ tristate "NVIDIA Tegra HD Audio"
+ depends on ARCH_TEGRA
+ select SND_HDA
+ help
+ Say Y here to support the HDA controller present in NVIDIA
+ Tegra SoCs
+
+ This options enables support for the HD Audio controller
+ present in some NVIDIA Tegra SoCs, used to communicate audio
+ to the HDMI output.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-tegra.
+
if SND_HDA
config SND_HDA_DSP_LOADER
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index d0d0c19..194f3093 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,5 +1,6 @@
snd-hda-intel-objs := hda_intel.o
snd-hda-controller-objs := hda_controller.o
+snd-hda-tegra-objs := hda_tegra.o
# for haswell power well
snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
@@ -47,3 +48,4 @@
# otherwise the codec patches won't be hooked before the PCI probe
# when built in kernel
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 1613388..589e47c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3722,7 +3722,7 @@
} else {
spec->multiout.slave_dig_outs = spec->slave_dig_outs;
if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
- break;
+ break;
spec->slave_dig_outs[nums - 1] = dig_nid;
}
nums++;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b540ad7..d3eaac0 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1724,7 +1724,7 @@
}
/* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
+static const struct pci_device_id azx_ids[] = {
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
new file mode 100644
index 0000000..086fd06
--- /dev/null
+++ b/sound/pci/hda/hda_tegra.c
@@ -0,0 +1,584 @@
+/*
+ *
+ * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "hda_codec.h"
+#include "hda_controller.h"
+#include "hda_priv.h"
+
+/* Defines for Nvidia Tegra HDA support */
+#define HDA_BAR0 0x8000
+
+#define HDA_CFG_CMD 0x1004
+#define HDA_CFG_BAR0 0x1010
+
+#define HDA_ENABLE_IO_SPACE (1 << 0)
+#define HDA_ENABLE_MEM_SPACE (1 << 1)
+#define HDA_ENABLE_BUS_MASTER (1 << 2)
+#define HDA_ENABLE_SERR (1 << 8)
+#define HDA_DISABLE_INTR (1 << 10)
+#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF
+#define HDA_BAR0_FINAL_PROGRAM (1 << 14)
+
+/* IPFS */
+#define HDA_IPFS_CONFIG 0x180
+#define HDA_IPFS_EN_FPCI 0x1
+
+#define HDA_IPFS_FPCI_BAR0 0x80
+#define HDA_FPCI_BAR0_START 0x40
+
+#define HDA_IPFS_INTR_MASK 0x188
+#define HDA_IPFS_EN_INTR (1 << 16)
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 1
+#define NUM_PLAYBACK_SD 1
+
+struct hda_tegra {
+ struct azx chip;
+ struct device *dev;
+ struct clk *hda_clk;
+ struct clk *hda2codec_2x_clk;
+ struct clk *hda2hdmi_clk;
+ void __iomem *regs;
+};
+
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+ "Automatic power-saving timeout (in seconds, 0 = disable).");
+
+/*
+ * DMA page allocation ops.
+ */
+static int dma_alloc_pages(struct azx *chip, int type, size_t size,
+ struct snd_dma_buffer *buf)
+{
+ return snd_dma_alloc_pages(type, chip->card->dev, size, buf);
+}
+
+static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+{
+ snd_dma_free_pages(buf);
+}
+
+static int substream_alloc_pages(struct azx *chip,
+ struct snd_pcm_substream *substream,
+ size_t size)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+ return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int substream_free_pages(struct azx *chip,
+ struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * Register access ops. Tegra HDA register access is DWORD only.
+ */
+static void hda_tegra_writel(u32 value, u32 *addr)
+{
+ writel(value, addr);
+}
+
+static u32 hda_tegra_readl(u32 *addr)
+{
+ return readl(addr);
+}
+
+static void hda_tegra_writew(u16 value, u16 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ v &= ~(0xffff << shift);
+ v |= value << shift;
+ writel(v, dword_addr);
+}
+
+static u16 hda_tegra_readw(u16 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ return (v >> shift) & 0xffff;
+}
+
+static void hda_tegra_writeb(u8 value, u8 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ v &= ~(0xff << shift);
+ v |= value << shift;
+ writel(v, dword_addr);
+}
+
+static u8 hda_tegra_readb(u8 *addr)
+{
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ u32 v;
+
+ v = readl(dword_addr);
+ return (v >> shift) & 0xff;
+}
+
+static const struct hda_controller_ops hda_tegra_ops = {
+ .reg_writel = hda_tegra_writel,
+ .reg_readl = hda_tegra_readl,
+ .reg_writew = hda_tegra_writew,
+ .reg_readw = hda_tegra_readw,
+ .reg_writeb = hda_tegra_writeb,
+ .reg_readb = hda_tegra_readb,
+ .dma_alloc_pages = dma_alloc_pages,
+ .dma_free_pages = dma_free_pages,
+ .substream_alloc_pages = substream_alloc_pages,
+ .substream_free_pages = substream_free_pages,
+};
+
+static void hda_tegra_init(struct hda_tegra *hda)
+{
+ u32 v;
+
+ /* Enable PCI access */
+ v = readl(hda->regs + HDA_IPFS_CONFIG);
+ v |= HDA_IPFS_EN_FPCI;
+ writel(v, hda->regs + HDA_IPFS_CONFIG);
+
+ /* Enable MEM/IO space and bus master */
+ v = readl(hda->regs + HDA_CFG_CMD);
+ v &= ~HDA_DISABLE_INTR;
+ v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
+ HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
+ writel(v, hda->regs + HDA_CFG_CMD);
+
+ writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
+
+ v = readl(hda->regs + HDA_IPFS_INTR_MASK);
+ v |= HDA_IPFS_EN_INTR;
+ writel(v, hda->regs + HDA_IPFS_INTR_MASK);
+}
+
+static int hda_tegra_enable_clocks(struct hda_tegra *data)
+{
+ int rc;
+
+ rc = clk_prepare_enable(data->hda_clk);
+ if (rc)
+ return rc;
+ rc = clk_prepare_enable(data->hda2codec_2x_clk);
+ if (rc)
+ goto disable_hda;
+ rc = clk_prepare_enable(data->hda2hdmi_clk);
+ if (rc)
+ goto disable_codec_2x;
+
+ return 0;
+
+disable_codec_2x:
+ clk_disable_unprepare(data->hda2codec_2x_clk);
+disable_hda:
+ clk_disable_unprepare(data->hda_clk);
+ return rc;
+}
+
+static void hda_tegra_disable_clocks(struct hda_tegra *data)
+{
+ clk_disable_unprepare(data->hda2hdmi_clk);
+ clk_disable_unprepare(data->hda2codec_2x_clk);
+ clk_disable_unprepare(data->hda_clk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * power management
+ */
+static int hda_tegra_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct azx_pcm *p;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ list_for_each_entry(p, &chip->pcm_list, list)
+ snd_pcm_suspend_all(p->pcm);
+ if (chip->initialized)
+ snd_hda_suspend(chip->bus);
+
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ hda_tegra_disable_clocks(hda);
+
+ return 0;
+}
+
+static int hda_tegra_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ int status;
+
+ hda_tegra_enable_clocks(hda);
+
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
+
+ hda_tegra_init(hda);
+
+ azx_init_chip(chip, 1);
+
+ snd_hda_resume(chip->bus);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops hda_tegra_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+};
+
+/*
+ * reboot notifier for hang-up problem at power-down
+ */
+static int hda_tegra_halt(struct notifier_block *nb, unsigned long event,
+ void *buf)
+{
+ struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+ snd_hda_bus_reboot_notify(chip->bus);
+ azx_stop_chip(chip);
+ return NOTIFY_OK;
+}
+
+static void hda_tegra_notifier_register(struct azx *chip)
+{
+ chip->reboot_notifier.notifier_call = hda_tegra_halt;
+ register_reboot_notifier(&chip->reboot_notifier);
+}
+
+static void hda_tegra_notifier_unregister(struct azx *chip)
+{
+ if (chip->reboot_notifier.notifier_call)
+ unregister_reboot_notifier(&chip->reboot_notifier);
+}
+
+/*
+ * destructor
+ */
+static int hda_tegra_dev_free(struct snd_device *device)
+{
+ int i;
+ struct azx *chip = device->device_data;
+
+ hda_tegra_notifier_unregister(chip);
+
+ if (chip->initialized) {
+ for (i = 0; i < chip->num_streams; i++)
+ azx_stream_stop(chip, &chip->azx_dev[i]);
+ azx_stop_chip(chip);
+ }
+
+ azx_free_stream_pages(chip);
+
+ return 0;
+}
+
+static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct device *dev = hda->dev;
+ struct resource *res;
+ int err;
+
+ hda->hda_clk = devm_clk_get(dev, "hda");
+ if (IS_ERR(hda->hda_clk))
+ return PTR_ERR(hda->hda_clk);
+ hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
+ if (IS_ERR(hda->hda2codec_2x_clk))
+ return PTR_ERR(hda->hda2codec_2x_clk);
+ hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
+ if (IS_ERR(hda->hda2hdmi_clk))
+ return PTR_ERR(hda->hda2hdmi_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hda->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
+
+ chip->remap_addr = hda->regs + HDA_BAR0;
+ chip->addr = res->start + HDA_BAR0;
+
+ err = hda_tegra_enable_clocks(hda);
+ if (err)
+ return err;
+
+ hda_tegra_init(hda);
+
+ return 0;
+}
+
+/*
+ * The codecs were powered up in snd_hda_codec_new().
+ * Now all initialization done, so turn them down if possible
+ */
+static void power_down_all_codecs(struct azx *chip)
+{
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &chip->bus->codec_list, list)
+ snd_hda_power_down(codec);
+}
+
+static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
+{
+ struct snd_card *card = chip->card;
+ int err;
+ unsigned short gcap;
+ int irq_id = platform_get_irq(pdev, 0);
+
+ err = hda_tegra_init_chip(chip, pdev);
+ if (err)
+ return err;
+
+ err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "unable to request IRQ %d, disabling device\n",
+ irq_id);
+ return err;
+ }
+ chip->irq = irq_id;
+
+ synchronize_irq(chip->irq);
+
+ gcap = azx_readw(chip, GCAP);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
+ /* gcap didn't give any info, switching to old method */
+ chip->playback_streams = NUM_PLAYBACK_SD;
+ chip->capture_streams = NUM_CAPTURE_SD;
+ }
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
+ chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams,
+ sizeof(*chip->azx_dev), GFP_KERNEL);
+ if (!chip->azx_dev)
+ return -ENOMEM;
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0)
+ return err;
+
+ /* initialize streams */
+ azx_init_stream(chip);
+
+ /* initialize chip */
+ azx_init_chip(chip, 1);
+
+ /* codec detection */
+ if (!chip->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ strcpy(card->driver, "tegra-hda");
+ strcpy(card->shortname, "tegra-hda");
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->addr, chip->irq);
+
+ return 0;
+}
+
+/*
+ * constructor
+ */
+static int hda_tegra_create(struct snd_card *card,
+ unsigned int driver_caps,
+ const struct hda_controller_ops *hda_ops,
+ struct hda_tegra *hda)
+{
+ static struct snd_device_ops ops = {
+ .dev_free = hda_tegra_dev_free,
+ };
+ struct azx *chip;
+ int err;
+
+ chip = &hda->chip;
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->ops = hda_ops;
+ chip->irq = -1;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
+ chip->dev_index = 0;
+ INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_LIST_HEAD(&chip->list);
+
+ chip->position_fix[0] = POS_FIX_AUTO;
+ chip->position_fix[1] = POS_FIX_AUTO;
+ chip->codec_probe_mask = -1;
+
+ chip->single_cmd = false;
+ chip->snoop = true;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id hda_tegra_match[] = {
+ { .compatible = "nvidia,tegra30-hda" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hda_tegra_match);
+
+static int hda_tegra_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct azx *chip;
+ struct hda_tegra *hda;
+ int err;
+ const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+ hda->dev = &pdev->dev;
+ chip = &hda->chip;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ dev_set_drvdata(&pdev->dev, card);
+
+ err = hda_tegra_first_init(chip, pdev);
+ if (err < 0)
+ goto out_free;
+
+ /* create codec instances */
+ err = azx_codec_create(chip, NULL, 0, &power_save);
+ if (err < 0)
+ goto out_free;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ goto out_free;
+
+ /* create PCM streams */
+ err = snd_hda_build_pcms(chip->bus);
+ if (err < 0)
+ goto out_free;
+
+ /* create mixer controls */
+ err = azx_mixer_create(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto out_free;
+
+ chip->running = 1;
+ power_down_all_codecs(chip);
+ hda_tegra_notifier_register(chip);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static int hda_tegra_remove(struct platform_device *pdev)
+{
+ return snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static struct platform_driver tegra_platform_hda = {
+ .driver = {
+ .name = "tegra-hda",
+ .pm = &hda_tegra_pm,
+ .of_match_table = hda_tegra_match,
+ },
+ .probe = hda_tegra_probe,
+ .remove = hda_tegra_remove,
+};
+module_platform_driver(tegra_platform_hda);
+
+MODULE_DESCRIPTION("Tegra HDA bus driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 1edbb9c..11d1c5d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1127,10 +1127,6 @@
AMP_OUT_UNMUTE);
eld = &per_pin->sink_eld;
- if (!eld->monitor_present) {
- hdmi_set_channel_count(codec, per_pin->cvt_nid, channels);
- return;
- }
if (!non_pcm && per_pin->chmap_set)
ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
@@ -3324,6 +3320,7 @@
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi },
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
@@ -3379,6 +3376,7 @@
MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0028");
MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 5f7c765..e603221 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -951,7 +951,9 @@
{ 0x10ec0280, 0x1028, 0, "ALC3220" },
{ 0x10ec0282, 0x1028, 0, "ALC3221" },
{ 0x10ec0283, 0x1028, 0, "ALC3223" },
+ { 0x10ec0288, 0x1028, 0, "ALC3263" },
{ 0x10ec0292, 0x1028, 0, "ALC3226" },
+ { 0x10ec0293, 0x1028, 0, "ALC3235" },
{ 0x10ec0255, 0x1028, 0, "ALC3234" },
{ 0x10ec0668, 0x1028, 0, "ALC3661" },
{ } /* terminator */
@@ -3522,6 +3524,7 @@
/* Direct Drive HP Amp control */
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x1b, 0x0c0b);
alc_write_coef_idx(codec, 0x45, 0xc429);
@@ -3538,6 +3541,25 @@
alc_write_coef_idx(codec, 0x18, 0x7308);
alc_write_coef_idx(codec, 0x6b, 0xc429);
break;
+ case 0x10ec0293:
+ /* SET Line1 JD to 0 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8);
+ /* SET charge pump by verb */
+ val = alc_read_coefex_idx(codec, 0x57, 0x05);
+ alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0);
+ /* SET EN_OSW to 1 */
+ val = alc_read_coefex_idx(codec, 0x57, 0x03);
+ alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) );
+ /* Combo JD gating with LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3));
+ /* Set to TRS type */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x15, 0x0d40);
alc_write_coef_idx(codec, 0xb7, 0x802b);
@@ -3561,6 +3583,7 @@
alc_write_coef_idx(codec, 0x06, 0x6100);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xc429);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -3576,6 +3599,21 @@
alc_write_coef_idx(codec, 0x19, 0xa208);
alc_write_coef_idx(codec, 0x2e, 0xacf0);
break;
+ case 0x10ec0293:
+ /* Set to TRS mode */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ /* SET charge pump by verb */
+ val = alc_read_coefex_idx(codec, 0x57, 0x05);
+ alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13));
+ /* SET EN_OSW to 0 */
+ val = alc_read_coefex_idx(codec, 0x57, 0x03);
+ alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0);
+ /* Combo JD gating without LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -3591,6 +3629,8 @@
static void alc_headset_mode_default(struct hda_codec *codec)
{
+ int val;
+
switch (codec->vendor_id) {
case 0x10ec0255:
alc_write_coef_idx(codec, 0x45, 0xc089);
@@ -3598,6 +3638,7 @@
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
alc_write_coef_idx(codec, 0x49, 0x0049);
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x06, 0x2100);
alc_write_coef_idx(codec, 0x32, 0x4ea3);
@@ -3608,6 +3649,16 @@
alc_write_coef_idx(codec, 0x6b, 0xc429);
alc_write_coef_idx(codec, 0x18, 0x7308);
break;
+ case 0x10ec0293:
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+ /* Set to TRS type */
+ alc_write_coef_idx(codec, 0x45, 0xC429);
+ /* Combo JD gating without LINE1-VREFO */
+ val = alc_read_coef_idx(codec, 0x1a);
+ alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0041);
alc_write_coef_idx(codec, 0x15, 0x0d40);
@@ -3620,6 +3671,8 @@
/* Iphone type */
static void alc_headset_mode_ctia(struct hda_codec *codec)
{
+ int val;
+
switch (codec->vendor_id) {
case 0x10ec0255:
/* Set to CTIA type */
@@ -3627,6 +3680,7 @@
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xd429);
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3637,6 +3691,13 @@
alc_write_coef_idx(codec, 0x76, 0x0008);
alc_write_coef_idx(codec, 0x18, 0x7388);
break;
+ case 0x10ec0293:
+ /* Set to ctia type */
+ alc_write_coef_idx(codec, 0x45, 0xd429);
+ /* SET Line1 JD to 1 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0x15, 0x0d60);
@@ -3649,6 +3710,8 @@
/* Nokia type */
static void alc_headset_mode_omtp(struct hda_codec *codec)
{
+ int val;
+
switch (codec->vendor_id) {
case 0x10ec0255:
/* Set to OMTP Type */
@@ -3656,6 +3719,7 @@
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xe429);
alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3666,6 +3730,13 @@
alc_write_coef_idx(codec, 0x76, 0x0008);
alc_write_coef_idx(codec, 0x18, 0x7388);
break;
+ case 0x10ec0293:
+ /* Set to omtp type */
+ alc_write_coef_idx(codec, 0x45, 0xe429);
+ /* SET Line1 JD to 1 */
+ val = alc_read_coef_idx(codec, 0x10);
+ alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0x15, 0x0d50);
@@ -3691,6 +3762,7 @@
val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x0070) == 0x0070;
break;
+ case 0x10ec0233:
case 0x10ec0283:
alc_write_coef_idx(codec, 0x45, 0xd029);
msleep(300);
@@ -3703,6 +3775,16 @@
val = alc_read_coef_idx(codec, 0x6c);
is_ctia = (val & 0x001c) == 0x001c;
break;
+ case 0x10ec0293:
+ /* Combo Jack auto detect */
+ val = alc_read_coef_idx(codec, 0x4a);
+ alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008);
+ /* Set to ctia type */
+ alc_write_coef_idx(codec, 0x45, 0xD429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
case 0x10ec0668:
alc_write_coef_idx(codec, 0x11, 0x0001);
alc_write_coef_idx(codec, 0xb7, 0x802b);
@@ -3894,6 +3976,16 @@
}
}
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ }
+}
+
static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4110,6 +4202,7 @@
ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_LENOVO_EAPD,
ALC275_FIXUP_SONY_HWEQ,
+ ALC275_FIXUP_SONY_DISABLE_AAMIX,
ALC271_FIXUP_DMIC,
ALC269_FIXUP_PCM_44K,
ALC269_FIXUP_STEREO_DMIC,
@@ -4159,6 +4252,8 @@
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_FIXUP_TPT440_DOCK,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -4213,6 +4308,12 @@
.chained = true,
.chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
},
+ [ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
[ALC271_FIXUP_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc271_fixup_dmic,
@@ -4552,6 +4653,26 @@
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
},
+ [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC292_FIXUP_TPT440_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -4609,6 +4730,8 @@
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x063e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0640, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0651, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -4626,6 +4749,8 @@
SND_PCI_QUIRK(0x1028, 0x067f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_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),
@@ -4699,6 +4824,7 @@
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(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
@@ -4712,7 +4838,8 @@
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -4790,6 +4917,7 @@
{.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
{.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
{.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
+ {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{}
};
@@ -5310,6 +5438,7 @@
ALC662_FIXUP_BASS_1A,
ALC662_FIXUP_BASS_CHMAP,
ALC668_FIXUP_AUTO_MUTE,
+ ALC668_FIXUP_DELL_DISABLE_AAMIX,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -5476,6 +5605,12 @@
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
+ [ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
[ALC668_FIXUP_AUTO_MUTE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_auto_mute_via_amp,
@@ -5536,7 +5671,7 @@
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(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_DISABLE_AAMIX),
SND_PCI_QUIRK(0x1028, 0x0623, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x0624, "Dell", ALC668_FIXUP_AUTO_MUTE),
SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 75515b4..7f40a15 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -795,7 +795,7 @@
}
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
- if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
+ if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
&spec->gpio_led_polarity,
&spec->gpio_led) == 2) {
unsigned int max_gpio;
@@ -808,7 +808,7 @@
spec->vref_mute_led_nid = spec->gpio_led;
return 1;
}
- if (sscanf(dev->name, "HP_Mute_LED_%d",
+ if (sscanf(dev->name, "HP_Mute_LED_%u",
&spec->gpio_led_polarity) == 1) {
set_hp_led_gpio(codec);
return 1;
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
index 04df83d..c241dc0 100644
--- a/sound/pci/lola/lola_proc.c
+++ b/sound/pci/lola/lola_proc.c
@@ -151,7 +151,7 @@
char line[64];
unsigned int id, verb, data, extdata;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
- if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
+ if (sscanf(line, "%u %u %u %u", &id, &verb, &data, &extdata) != 4)
continue;
lola_codec_read(chip, id, verb, data, extdata,
&chip->debug_res,
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index 2d8e95e..e8f38e5 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -24,6 +24,7 @@
/* #define RMH_DEBUG 1 */
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
@@ -429,11 +430,6 @@
return ret;
}
-#define CSES_TIMEOUT 100 /* microseconds */
-#define CSES_CE 0x0001
-#define CSES_BROADCAST 0x0002
-#define CSES_UPDATE_LDSV 0x0004
-
#define PIPE_INFO_TO_CMD(capture, pipe) \
((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
@@ -519,7 +515,6 @@
*r_needed += 1;
}
-#if 0
dev_dbg(chip->card->dev,
"CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
*r_needed, *r_freed);
@@ -530,7 +525,6 @@
chip->rmh.stat[i],
chip->rmh.stat[i] & MASK_DATA_SIZE);
}
-#endif
}
spin_unlock_irqrestore(&chip->msg_lock, flags);
@@ -971,9 +965,9 @@
/* interrupt handling */
#define PCX_IRQ_NONE 0
-#define IRQCS_ACTIVE_PCIDB 0x00002000L /* Bit nø 13 */
-#define IRQCS_ENABLE_PCIIRQ 0x00000100L /* Bit nø 08 */
-#define IRQCS_ENABLE_PCIDB 0x00000200L /* Bit nø 09 */
+#define IRQCS_ACTIVE_PCIDB BIT(13)
+#define IRQCS_ENABLE_PCIIRQ BIT(8)
+#define IRQCS_ENABLE_PCIDB BIT(9)
static u32 lx_interrupt_test_ack(struct lx6464es *chip)
{
@@ -1030,25 +1024,21 @@
int err;
u32 stat[9]; /* answer from CMD_04_GET_EVENT */
- /* On peut optimiser pour ne pas lire les evenements vides
- * les mots de réponse sont dans l'ordre suivant :
- * Stat[0] mot de status général
- * Stat[1] fin de buffer OUT pF
- * Stat[2] fin de buffer OUT pf
- * Stat[3] fin de buffer IN pF
- * Stat[4] fin de buffer IN pf
- * Stat[5] underrun poid fort
- * Stat[6] underrun poid faible
- * Stat[7] overrun poid fort
- * Stat[8] overrun poid faible
+ /* We can optimize this to not read dumb events.
+ * Answer words are in the following order:
+ * Stat[0] general status
+ * Stat[1] end of buffer OUT pF
+ * Stat[2] end of buffer OUT pf
+ * Stat[3] end of buffer IN pF
+ * Stat[4] end of buffer IN pf
+ * Stat[5] MSB underrun
+ * Stat[6] LSB underrun
+ * Stat[7] MSB overrun
+ * Stat[8] LSB overrun
* */
u64 orun_mask;
u64 urun_mask;
-#if 0
- int has_underrun = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
- int has_overrun = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
-#endif
int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
@@ -1199,9 +1189,8 @@
if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
goto exit;
-#if 0
if (irqsrc & MASK_SYS_STATUS_EOBI)
- dev_dgg(chip->card->dev, "interrupt: EOBI\n");
+ dev_dbg(chip->card->dev, "interrupt: EOBI\n");
if (irqsrc & MASK_SYS_STATUS_EOBO)
dev_dbg(chip->card->dev, "interrupt: EOBO\n");
@@ -1211,7 +1200,6 @@
if (irqsrc & MASK_SYS_STATUS_ORUN)
dev_dbg(chip->card->dev, "interrupt: ORUN\n");
-#endif
if (async_pending) {
u64 notified_in_pipe_mask = 0;
@@ -1238,7 +1226,6 @@
}
if (async_escmd) {
-#if 0
/* backdoor for ethersound commands
*
* for now, we do not need this
@@ -1246,7 +1233,6 @@
* */
dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
-#endif
}
exit:
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index e05a86b..d393153 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -147,5 +147,18 @@
To compile this driver as a module, choose M here: the module
will be called snd-usb-hiface.
+config SND_BCD2000
+ tristate "Behringer BCD2000 MIDI driver"
+ select SND_RAWMIDI
+ help
+ Say Y here to include MIDI support for the Behringer BCD2000 DJ
+ controller.
+
+ Audio support is still work-in-progress at
+ https://github.com/anyc/snd-usb-bcd2000
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bcd2000.
+
endif # SND_USB
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index abe668f..2b92f0d 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -23,4 +23,4 @@
obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
diff --git a/sound/usb/bcd2000/Makefile b/sound/usb/bcd2000/Makefile
new file mode 100644
index 0000000..f09ccc0
--- /dev/null
+++ b/sound/usb/bcd2000/Makefile
@@ -0,0 +1,3 @@
+snd-bcd2000-y := bcd2000.o
+
+obj-$(CONFIG_SND_BCD2000) += snd-bcd2000.o
\ No newline at end of file
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
new file mode 100644
index 0000000..820d6ca
--- /dev/null
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -0,0 +1,461 @@
+/*
+ * Behringer BCD2000 driver
+ *
+ * Copyright (C) 2014 Mario Kicherer (dev@kicherer.org)
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#define PREFIX "snd-bcd2000: "
+#define BUFSIZE 64
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x1397, 0x00bd) },
+ { },
+};
+
+static unsigned char device_cmd_prefix[] = {0x03, 0x00};
+
+static unsigned char bcd2000_init_sequence[] = {
+ 0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81,
+ 0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7,
+ 0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x18, 0xfa, 0x11, 0xff, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf2, 0x34, 0x4a, 0xf7,
+ 0x18, 0xfa, 0x11, 0xff
+};
+
+struct bcd2000 {
+ struct usb_device *dev;
+ struct snd_card *card;
+ struct usb_interface *intf;
+ int card_index;
+
+ int midi_out_active;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_receive_substream;
+ struct snd_rawmidi_substream *midi_out_substream;
+
+ unsigned char midi_in_buf[BUFSIZE];
+ unsigned char midi_out_buf[BUFSIZE];
+
+ struct urb *midi_out_urb;
+ struct urb *midi_in_urb;
+
+ struct usb_anchor anchor;
+};
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+
+static DEFINE_MUTEX(devices_mutex);
+DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+static struct usb_driver bcd2000_driver;
+
+#ifdef CONFIG_SND_DEBUG
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len)
+{
+ print_hex_dump(KERN_DEBUG, prefix,
+ DUMP_PREFIX_NONE, 16, 1,
+ buf, len, false);
+}
+#else
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len) {}
+#endif
+
+static int bcd2000_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int bcd2000_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+ bcd2k->midi_receive_substream = up ? substream : NULL;
+}
+
+static void bcd2000_midi_handle_input(struct bcd2000 *bcd2k,
+ const unsigned char *buf, unsigned int buf_len)
+{
+ unsigned int payload_length, tocopy;
+ struct snd_rawmidi_substream *midi_receive_substream;
+
+ midi_receive_substream = ACCESS_ONCE(bcd2k->midi_receive_substream);
+ if (!midi_receive_substream)
+ return;
+
+ bcd2000_dump_buffer(PREFIX "received from device: ", buf, buf_len);
+
+ if (buf_len < 2)
+ return;
+
+ payload_length = buf[0];
+
+ /* ignore packets without payload */
+ if (payload_length == 0)
+ return;
+
+ tocopy = min(payload_length, buf_len-1);
+
+ bcd2000_dump_buffer(PREFIX "sending to userspace: ",
+ &buf[1], tocopy);
+
+ snd_rawmidi_receive(midi_receive_substream,
+ &buf[1], tocopy);
+}
+
+static void bcd2000_midi_send(struct bcd2000 *bcd2k)
+{
+ int len, ret;
+ struct snd_rawmidi_substream *midi_out_substream;
+
+ BUILD_BUG_ON(sizeof(device_cmd_prefix) >= BUFSIZE);
+
+ midi_out_substream = ACCESS_ONCE(bcd2k->midi_out_substream);
+ if (!midi_out_substream)
+ return;
+
+ /* copy command prefix bytes */
+ memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
+ sizeof(device_cmd_prefix));
+
+ /*
+ * get MIDI packet and leave space for command prefix
+ * and payload length
+ */
+ len = snd_rawmidi_transmit(midi_out_substream,
+ bcd2k->midi_out_buf + 3, BUFSIZE - 3);
+
+ if (len < 0)
+ dev_err(&bcd2k->dev->dev, "%s: snd_rawmidi_transmit error %d\n",
+ __func__, len);
+
+ if (len <= 0)
+ return;
+
+ /* set payload length */
+ bcd2k->midi_out_buf[2] = len;
+ bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
+
+ bcd2000_dump_buffer(PREFIX "sending to device: ",
+ bcd2k->midi_out_buf, len+3);
+
+ /* send packet to the BCD2000 */
+ ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s (%p): usb_submit_urb() failed, ret=%d, len=%d\n",
+ __func__, midi_out_substream, ret, len);
+ else
+ bcd2k->midi_out_active = 1;
+}
+
+static int bcd2000_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+ if (bcd2k->midi_out_active) {
+ usb_kill_urb(bcd2k->midi_out_urb);
+ bcd2k->midi_out_active = 0;
+ }
+
+ return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+ if (up) {
+ bcd2k->midi_out_substream = substream;
+ /* check if there is data userspace wants to send */
+ if (!bcd2k->midi_out_active)
+ bcd2000_midi_send(bcd2k);
+ } else {
+ bcd2k->midi_out_substream = NULL;
+ }
+}
+
+static void bcd2000_output_complete(struct urb *urb)
+{
+ struct bcd2000 *bcd2k = urb->context;
+
+ bcd2k->midi_out_active = 0;
+
+ if (urb->status)
+ dev_warn(&urb->dev->dev,
+ PREFIX "output urb->status: %d\n", urb->status);
+
+ if (urb->status == -ESHUTDOWN)
+ return;
+
+ /* check if there is more data userspace wants to send */
+ bcd2000_midi_send(bcd2k);
+}
+
+static void bcd2000_input_complete(struct urb *urb)
+{
+ int ret;
+ struct bcd2000 *bcd2k = urb->context;
+
+ if (urb->status)
+ dev_warn(&urb->dev->dev,
+ PREFIX "input urb->status: %i\n", urb->status);
+
+ if (!bcd2k || urb->status == -ESHUTDOWN)
+ return;
+
+ if (urb->actual_length > 0)
+ bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer,
+ urb->actual_length);
+
+ /* return URB to device */
+ ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_ATOMIC);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() failed, ret=%d\n",
+ __func__, ret);
+}
+
+static struct snd_rawmidi_ops bcd2000_midi_output = {
+ .open = bcd2000_midi_output_open,
+ .close = bcd2000_midi_output_close,
+ .trigger = bcd2000_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops bcd2000_midi_input = {
+ .open = bcd2000_midi_input_open,
+ .close = bcd2000_midi_input_close,
+ .trigger = bcd2000_midi_input_trigger,
+};
+
+static void bcd2000_init_device(struct bcd2000 *bcd2k)
+{
+ int ret;
+
+ init_usb_anchor(&bcd2k->anchor);
+ usb_anchor_urb(bcd2k->midi_out_urb, &bcd2k->anchor);
+ usb_anchor_urb(bcd2k->midi_in_urb, &bcd2k->anchor);
+
+ /* copy init sequence into buffer */
+ memcpy(bcd2k->midi_out_buf, bcd2000_init_sequence, 52);
+ bcd2k->midi_out_urb->transfer_buffer_length = 52;
+
+ /* submit sequence */
+ ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_KERNEL);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() out failed, ret=%d: ",
+ __func__, ret);
+ else
+ bcd2k->midi_out_active = 1;
+
+ /* pass URB to device to enable button and controller events */
+ ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_KERNEL);
+ if (ret < 0)
+ dev_err(&bcd2k->dev->dev, PREFIX
+ "%s: usb_submit_urb() in failed, ret=%d: ",
+ __func__, ret);
+
+ /* ensure initialization is finished */
+ usb_wait_anchor_empty_timeout(&bcd2k->anchor, 1000);
+}
+
+static int bcd2000_init_midi(struct bcd2000 *bcd2k)
+{
+ int ret;
+ struct snd_rawmidi *rmidi;
+
+ ret = snd_rawmidi_new(bcd2k->card, bcd2k->card->shortname, 0,
+ 1, /* output */
+ 1, /* input */
+ &rmidi);
+
+ if (ret < 0)
+ return ret;
+
+ strlcpy(rmidi->name, bcd2k->card->shortname, sizeof(rmidi->name));
+
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = bcd2k;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &bcd2000_midi_output);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &bcd2000_midi_input);
+
+ bcd2k->rmidi = rmidi;
+
+ bcd2k->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ bcd2k->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!bcd2k->midi_in_urb || !bcd2k->midi_out_urb) {
+ dev_err(&bcd2k->dev->dev, PREFIX "usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+
+ usb_fill_int_urb(bcd2k->midi_in_urb, bcd2k->dev,
+ usb_rcvintpipe(bcd2k->dev, 0x81),
+ bcd2k->midi_in_buf, BUFSIZE,
+ bcd2000_input_complete, bcd2k, 1);
+
+ usb_fill_int_urb(bcd2k->midi_out_urb, bcd2k->dev,
+ usb_sndintpipe(bcd2k->dev, 0x1),
+ bcd2k->midi_out_buf, BUFSIZE,
+ bcd2000_output_complete, bcd2k, 1);
+
+ bcd2000_init_device(bcd2k);
+
+ return 0;
+}
+
+static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
+ struct usb_interface *interface)
+{
+ /* usb_kill_urb not necessary, urb is aborted automatically */
+
+ usb_free_urb(bcd2k->midi_out_urb);
+ usb_free_urb(bcd2k->midi_in_urb);
+
+ if (bcd2k->intf) {
+ usb_set_intfdata(bcd2k->intf, NULL);
+ bcd2k->intf = NULL;
+ }
+}
+
+static int bcd2000_probe(struct usb_interface *interface,
+ const struct usb_device_id *usb_id)
+{
+ struct snd_card *card;
+ struct bcd2000 *bcd2k;
+ unsigned int card_index;
+ char usb_path[32];
+ int err;
+
+ mutex_lock(&devices_mutex);
+
+ for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
+ if (!test_bit(card_index, devices_used))
+ break;
+
+ if (card_index >= SNDRV_CARDS) {
+ mutex_unlock(&devices_mutex);
+ return -ENOENT;
+ }
+
+ err = snd_card_new(&interface->dev, index[card_index], id[card_index],
+ THIS_MODULE, sizeof(*bcd2k), &card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return err;
+ }
+
+ bcd2k = card->private_data;
+ bcd2k->dev = interface_to_usbdev(interface);
+ bcd2k->card = card;
+ bcd2k->card_index = card_index;
+ bcd2k->intf = interface;
+
+ snd_card_set_dev(card, &interface->dev);
+
+ strncpy(card->driver, "snd-bcd2000", sizeof(card->driver));
+ strncpy(card->shortname, "BCD2000", sizeof(card->shortname));
+ usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path));
+ snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname),
+ "Behringer BCD2000 at %s",
+ usb_path);
+
+ err = bcd2000_init_midi(bcd2k);
+ if (err < 0)
+ goto probe_error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto probe_error;
+
+ usb_set_intfdata(interface, bcd2k);
+ set_bit(card_index, devices_used);
+
+ mutex_unlock(&devices_mutex);
+ return 0;
+
+probe_error:
+ dev_info(&bcd2k->dev->dev, PREFIX "error during probing");
+ bcd2000_free_usb_related_resources(bcd2k, interface);
+ snd_card_free(card);
+ mutex_unlock(&devices_mutex);
+ return err;
+}
+
+static void bcd2000_disconnect(struct usb_interface *interface)
+{
+ struct bcd2000 *bcd2k = usb_get_intfdata(interface);
+
+ if (!bcd2k)
+ return;
+
+ mutex_lock(&devices_mutex);
+
+ /* make sure that userspace cannot create new requests */
+ snd_card_disconnect(bcd2k->card);
+
+ bcd2000_free_usb_related_resources(bcd2k, interface);
+
+ clear_bit(bcd2k->card_index, devices_used);
+
+ snd_card_free_when_closed(bcd2k->card);
+
+ mutex_unlock(&devices_mutex);
+}
+
+static struct usb_driver bcd2000_driver = {
+ .name = "snd-bcd2000",
+ .probe = bcd2000_probe,
+ .disconnect = bcd2000_disconnect,
+ .id_table = id_table,
+};
+
+module_usb_driver(bcd2000_driver);
+
+MODULE_DEVICE_TABLE(usb, id_table);
+MODULE_AUTHOR("Mario Kicherer, dev@kicherer.org");
+MODULE_DESCRIPTION("Behringer BCD2000 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index d40a285..6fe83e4 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1986,7 +1986,7 @@
if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
if (! len)
- sprintf(namelist[i], "Input %d", i);
+ sprintf(namelist[i], "Input %u", i);
}
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);