From 375d135818f32bbe7b3f071bd54d977c4ff8d84a Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 19 Mar 2011 16:32:53 +0100 Subject: ALSA: tea575x-tuner: various improvements Improve tea575x-tuner with various good things from radio-maestro: - extend frequency range to 50-150MHz - fix querycap(): card name, CAP_RADIO - improve g_tuner(): CAP_STEREO, stereo and tuned indication - improve g_frequency(): tuner index checking and reading frequency from HW - improve s_frequency(): tuner index and type checking Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 6 +++-- sound/i2c/other/tea575x-tuner.c | 52 ++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 5718a02d3af..3d6cdd80df5 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -38,8 +38,10 @@ struct snd_tea575x { struct snd_card *card; struct video_device *vd; /* video device */ int dev_nr; /* requested device number + 1 */ - int tea5759; /* 5759 chip is present */ - int mute; /* Device is muted? */ + bool tea5759; /* 5759 chip is present */ + bool mute; /* Device is muted? */ + bool stereo; /* receiving stereo */ + bool tuned; /* tuned to a station */ unsigned int freq_fixup; /* crystal onboard */ unsigned int val; /* hw value */ unsigned long freq; /* frequency */ diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index ee538f1ae84..9f35f378a17 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -37,8 +37,8 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); #define RADIO_VERSION KERNEL_VERSION(0, 0, 2) -#define FREQ_LO (87 * 16000) -#define FREQ_HI (108 * 16000) +#define FREQ_LO (50UL * 16000) +#define FREQ_HI (150UL * 16000) /* * definitions @@ -77,15 +77,29 @@ static struct v4l2_queryctrl radio_qctrl[] = { * lowlevel part */ +static void snd_tea575x_get_freq(struct snd_tea575x *tea) +{ + unsigned long freq; + + freq = tea->ops->read(tea) & TEA575X_BIT_FREQ_MASK; + /* freq *= 12.5 */ + freq *= 125; + freq /= 10; + /* crystal fixup */ + if (tea->tea5759) + freq += tea->freq_fixup; + else + freq -= tea->freq_fixup; + + tea->freq = freq * 16; /* from kHz */ +} + static void snd_tea575x_set_freq(struct snd_tea575x *tea) { unsigned long freq; - freq = tea->freq / 16; /* to kHz */ - if (freq > 108000) - freq = 108000; - if (freq < 87000) - freq = 87000; + freq = clamp(tea->freq, FREQ_LO, FREQ_HI); + freq /= 16; /* to kHz */ /* crystal fixup */ if (tea->tea5759) freq -= tea->freq_fixup; @@ -109,29 +123,33 @@ static int vidioc_querycap(struct file *file, void *priv, { struct snd_tea575x *tea = video_drvdata(file); - strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757"); strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); - strlcpy(v->card, "Maestro Radio", sizeof(v->card)); + strlcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757", sizeof(v->card)); sprintf(v->bus_info, "PCI"); v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; } static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { + struct snd_tea575x *tea = video_drvdata(file); + if (v->index > 0) return -EINVAL; + tea->ops->read(tea); + strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; v->rangelow = FREQ_LO; v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + v->signal = tea->tuned ? 0xffff : 0; + return 0; } @@ -148,7 +166,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct snd_tea575x *tea = video_drvdata(file); + if (f->tuner != 0) + return -EINVAL; f->type = V4L2_TUNER_RADIO; + snd_tea575x_get_freq(tea); f->frequency = tea->freq; return 0; } @@ -158,6 +179,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, { struct snd_tea575x *tea = video_drvdata(file); + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) return -EINVAL; -- cgit v1.2.3 From f8960d61bc8ba945b07a4de1288aac5d52f8607b Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 19 Mar 2011 16:33:01 +0100 Subject: ALSA: tea575x-tuner: remove dev_nr Remove unused dev_nr from struct tea575x_tuner. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 1 - sound/pci/fm801.c | 1 - 2 files changed, 2 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 3d6cdd80df5..5aade569439 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -37,7 +37,6 @@ struct snd_tea575x_ops { struct snd_tea575x { struct snd_card *card; struct video_device *vd; /* video device */ - int dev_nr; /* requested device number + 1 */ bool tea5759; /* 5759 chip is present */ bool mute; /* Device is muted? */ bool stereo; /* receiving stereo */ diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index e1baad74ea4..f4dc1c77202 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1453,7 +1453,6 @@ static int __devinit snd_fm801_create(struct snd_card *card, #ifdef TEA575X_RADIO if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && (tea575x_tuner & TUNER_TYPE_MASK) < 4) { - chip->tea.dev_nr = tea575x_tuner >> 16; chip->tea.card = card; chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; -- cgit v1.2.3 From 1872f589951caee1afd7cd2ea6729ac892de9ddf Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 19 Mar 2011 16:33:14 +0100 Subject: ALSA: es1968: add radio (tea575x tuner) support Add TEA5757 radio tuner support to es1968 driver. This is found at least on MediaForte SF64-PCE2 sound cards. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/i2c/other/Makefile | 2 +- sound/pci/Kconfig | 14 ++++-- sound/pci/es1968.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile index 2dad40f3f62..c95d8f1aae8 100644 --- a/sound/i2c/other/Makefile +++ b/sound/i2c/other/Makefile @@ -14,4 +14,4 @@ snd-tea575x-tuner-objs := tea575x-tuner.o obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o -obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o +obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 389cd793166..f9f533fcccc 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -534,6 +534,14 @@ config SND_ES1968_INPUT If you say N the buttons will directly control the master volume. It is recommended to say Y. +config SND_ES1968_RADIO + bool "Enable TEA5757 radio tuner support for es1968" + depends on SND_ES1968 + depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968 + help + Say Y here to include support for TEA5757 radio tuner integrated on + some MediaForte cards (e.g. SF64-PCE2). + config SND_FM801 tristate "ForteMedia FM801" select SND_OPL3_LIB @@ -555,10 +563,10 @@ config SND_FM801_TEA575X_BOOL FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media Forte SF256-PCS-02) into the snd-fm801 driver. -config SND_FM801_TEA575X +config SND_TEA575X tristate - depends on SND_FM801_TEA575X_BOOL - default SND_FM801 + depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO + default SND_FM801 || SND_ES1968 source "sound/pci/hda/Kconfig" diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 7c17f45d876..faf9138970a 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -112,6 +112,10 @@ #include #include +#ifdef CONFIG_SND_ES1968_RADIO +#include +#endif + #define CARD_NAME "ESS Maestro1/2" #define DRIVER_NAME "ES1968" @@ -553,6 +557,10 @@ struct es1968 { spinlock_t ac97_lock; struct tasklet_struct hwvol_tq; #endif + +#ifdef CONFIG_SND_ES1968_RADIO + struct snd_tea575x tea; +#endif }; static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id); @@ -2571,6 +2579,111 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip) } #endif /* CONFIG_SND_ES1968_INPUT */ +#ifdef CONFIG_SND_ES1968_RADIO +#define GPIO_DATA 0x60 +#define IO_MASK 4 /* mask register offset from GPIO_DATA + bits 1=unmask write to given bit */ +#define IO_DIR 8 /* direction register offset from GPIO_DATA + bits 0/1=read/write direction */ +/* mask bits for GPIO lines */ +#define STR_DATA 0x0040 /* GPIO6 */ +#define STR_CLK 0x0080 /* GPIO7 */ +#define STR_WREN 0x0100 /* GPIO8 */ +#define STR_MOST 0x0200 /* GPIO9 */ + +static void snd_es1968_tea575x_write(struct snd_tea575x *tea, unsigned int val) +{ + struct es1968 *chip = tea->private_data; + unsigned long io = chip->io_port + GPIO_DATA; + u16 l, bits; + u16 omask, odir; + + omask = inw(io + IO_MASK); + odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); + outw(odir | STR_DATA, io + IO_DIR); + outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); + udelay(16); + + for (l = 25; l; l--) { + bits = ((val >> 18) & STR_DATA) | STR_WREN; + val <<= 1; /* shift data */ + outw(bits, io); /* start strobe */ + udelay(2); + outw(bits | STR_CLK, io); /* HI level */ + udelay(2); + outw(bits, io); /* LO level */ + udelay(4); + } + + if (!tea->mute) + outw(0, io); + + udelay(4); + outw(omask, io + IO_MASK); + outw(odir, io + IO_DIR); + msleep(125); +} + +static unsigned int snd_es1968_tea575x_read(struct snd_tea575x *tea) +{ + struct es1968 *chip = tea->private_data; + unsigned long io = chip->io_port + GPIO_DATA; + u16 l, rdata; + u32 data = 0; + u16 omask; + + omask = inw(io + IO_MASK); + outw(~(STR_CLK | STR_WREN), io + IO_MASK); + outw(0, io); + udelay(16); + + for (l = 24; l--;) { + outw(STR_CLK, io); /* HI state */ + udelay(2); + if (!l) + tea->tuned = inw(io) & STR_MOST ? 0 : 1; + outw(0, io); /* LO state */ + udelay(2); + data <<= 1; /* shift data */ + rdata = inw(io); + if (!l) + tea->stereo = (rdata & STR_MOST) ? 0 : 1; + else if (l && rdata & STR_DATA) + data++; + udelay(2); + } + + if (tea->mute) + outw(STR_WREN, io); + + udelay(4); + outw(omask, io + IO_MASK); + + return data & 0x3ffe; +} + +static void snd_es1968_tea575x_mute(struct snd_tea575x *tea, unsigned int mute) +{ + struct es1968 *chip = tea->private_data; + unsigned long io = chip->io_port + GPIO_DATA; + u16 omask; + + omask = inw(io + IO_MASK); + outw(~STR_WREN, io + IO_MASK); + tea->mute = mute; + outw(tea->mute ? STR_WREN : 0, io); + udelay(4); + outw(omask, io + IO_MASK); + msleep(125); +} + +static struct snd_tea575x_ops snd_es1968_tea_ops = { + .write = snd_es1968_tea575x_write, + .read = snd_es1968_tea575x_read, + .mute = snd_es1968_tea575x_mute, +}; +#endif + static int snd_es1968_free(struct es1968 *chip) { #ifdef CONFIG_SND_ES1968_INPUT @@ -2585,6 +2698,10 @@ static int snd_es1968_free(struct es1968 *chip) outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */ } +#ifdef CONFIG_SND_ES1968_RADIO + snd_tea575x_exit(&chip->tea); +#endif + if (chip->irq >= 0) free_irq(chip->irq, chip); snd_es1968_free_gameport(chip); @@ -2723,6 +2840,14 @@ static int __devinit snd_es1968_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); +#ifdef CONFIG_SND_ES1968_RADIO + chip->tea.card = card; + chip->tea.freq_fixup = 10700; + chip->tea.private_data = chip; + chip->tea.ops = &snd_es1968_tea_ops; + snd_tea575x_init(&chip->tea); +#endif + *chip_ret = chip; return 0; -- cgit v1.2.3 From 66b5b9722b8743f83d4c3f11f39665f5f2c40b12 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 16 Mar 2011 12:16:39 +0000 Subject: ALSA: Add snd_ctl_replace() to dynamically replace a control Add a function to dynamically replace a given control. If the control does not already exist, a third parameter is used to determine whether to actually add that control. This is useful in cases where downloadable firmware at runtime can add or replace existing controls. A separate patch needs to be made to allow ALSA Mixer to render the replaced controls on the fly. Signed-off-by: Dimitris Papastamos Signed-off-by: Takashi Iwai --- include/sound/control.h | 1 + sound/core/control.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/include/sound/control.h b/include/sound/control.h index e67db286936..40423810985 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -113,6 +113,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, v void snd_ctl_free_one(struct snd_kcontrol * kcontrol); int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol); +int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace); int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id); int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id); int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, diff --git a/sound/core/control.c b/sound/core/control.c index a08ad57c49b..5d98194bcad 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -365,6 +365,70 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) EXPORT_SYMBOL(snd_ctl_add); +/** + * snd_ctl_replace - replace the control instance of the card + * @card: the card instance + * @kcontrol: the control instance to replace + * @add_on_replace: add the control if not already added + * + * Replaces the given control. If the given control does not exist + * 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. + */ +int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, + bool add_on_replace) +{ + struct snd_ctl_elem_id id; + unsigned int idx; + struct snd_kcontrol *old; + int ret; + + if (!kcontrol) + return -EINVAL; + if (snd_BUG_ON(!card || !kcontrol->info)) { + ret = -EINVAL; + goto error; + } + id = kcontrol->id; + down_write(&card->controls_rwsem); + old = snd_ctl_find_id(card, &id); + if (!old) { + if (add_on_replace) + goto add; + up_write(&card->controls_rwsem); + ret = -EINVAL; + goto error; + } + ret = snd_ctl_remove(card, old); + if (ret < 0) { + up_write(&card->controls_rwsem); + goto error; + } +add: + if (snd_ctl_find_hole(card, kcontrol->count) < 0) { + up_write(&card->controls_rwsem); + ret = -ENOMEM; + goto error; + } + list_add_tail(&kcontrol->list, &card->controls); + card->controls_count += kcontrol->count; + kcontrol->id.numid = card->last_numid + 1; + card->last_numid += kcontrol->count; + up_write(&card->controls_rwsem); + for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id); + return 0; + +error: + snd_ctl_free_one(kcontrol); + return ret; +} +EXPORT_SYMBOL(snd_ctl_replace); + /** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance -- cgit v1.2.3 From b21a8ee67013372f439fbd1591e91d09de49bb9c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 19 Mar 2011 17:23:47 +0100 Subject: [media] remove radio-maestro Remove broken radio-maestro driver as the radio functionality is now integrated in the es1968 driver. Signed-off-by: Ondrej Zary Acked-by: Mauro Carvalho Chehab Acked-by: Hans Verkuil Signed-off-by: Takashi Iwai --- drivers/media/radio/Kconfig | 15 -- drivers/media/radio/Makefile | 1 - drivers/media/radio/radio-maestro.c | 452 ------------------------------------ 3 files changed, 468 deletions(-) delete mode 100644 drivers/media/radio/radio-maestro.c diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index ecdffa6aac6..851716a7da0 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -166,21 +166,6 @@ config RADIO_MAXIRADIO To compile this driver as a module, choose M here: the module will be called radio-maxiradio. -config RADIO_MAESTRO - tristate "Maestro on board radio" - depends on VIDEO_V4L2 && PCI - ---help--- - Say Y here to directly support the on-board radio tuner on the - Maestro 2 or 2E sound card. - - In order to control your radio card, you will need to use programs - that are compatible with the Video For Linux API. Information on - this API and pointers to "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called radio-maestro. - config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" depends on ISA && VIDEO_V4L2 && SND diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 717656d2f74..d68907fc792 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o -obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c deleted file mode 100644 index 6af61bfeb17..00000000000 --- a/drivers/media/radio/radio-maestro.c +++ /dev/null @@ -1,452 +0,0 @@ -/* Maestro PCI sound card radio driver for Linux support - * (c) 2000 A. Tlalka, atlka@pg.gda.pl - * Notes on the hardware - * - * + Frequency control is done digitally - * + No volume control - only mute/unmute - you have to use Aux line volume - * control on Maestro card to set the volume - * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after - * frequency setting (>100ms) and only when the radio is unmuted. - * version 0.02 - * + io port is automatically detected - only the first radio is used - * version 0.03 - * + thread access locking additions - * version 0.04 - * + code improvements - * + VIDEO_TUNER_LOW is permanent - * - * Converted to V4L2 API by Mauro Carvalho Chehab - */ - -#include -#include -#include -#include -#include /* for KERNEL_VERSION MACRO */ -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl"); -MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio."); -MODULE_LICENSE("GPL"); - -static int radio_nr = -1; -module_param(radio_nr, int, 0); - -#define RADIO_VERSION KERNEL_VERSION(0, 0, 6) -#define DRIVER_VERSION "0.06" - -#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */ - -#define IO_MASK 4 /* mask register offset from GPIO_DATA - bits 1=unmask write to given bit */ -#define IO_DIR 8 /* direction register offset from GPIO_DATA - bits 0/1=read/write direction */ - -#define GPIO6 0x0040 /* mask bits for GPIO lines */ -#define GPIO7 0x0080 -#define GPIO8 0x0100 -#define GPIO9 0x0200 - -#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */ -#define STR_CLK GPIO7 -#define STR_WREN GPIO8 -#define STR_MOST GPIO9 - -#define FREQ_LO 50*16000 -#define FREQ_HI 150*16000 - -#define FREQ_IF 171200 /* 10.7*16000 */ -#define FREQ_STEP 200 /* 12.5*16 */ - -#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\ - /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */ - -#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) - -struct maestro { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct pci_dev *pdev; - struct mutex lock; - - u16 io; /* base of Maestro card radio io (GPIO_DATA)*/ - u16 muted; /* VIDEO_AUDIO_MUTE */ - u16 stereo; /* VIDEO_TUNER_STEREO_ON */ - u16 tuned; /* signal strength (0 or 0xffff) */ -}; - -static inline struct maestro *to_maestro(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct maestro, v4l2_dev); -} - -static u32 radio_bits_get(struct maestro *dev) -{ - u16 io = dev->io, l, rdata; - u32 data = 0; - u16 omask; - - omask = inw(io + IO_MASK); - outw(~(STR_CLK | STR_WREN), io + IO_MASK); - outw(0, io); - udelay(16); - - for (l = 24; l--;) { - outw(STR_CLK, io); /* HI state */ - udelay(2); - if (!l) - dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; - outw(0, io); /* LO state */ - udelay(2); - data <<= 1; /* shift data */ - rdata = inw(io); - if (!l) - dev->stereo = (rdata & STR_MOST) ? 0 : 1; - else if (rdata & STR_DATA) - data++; - udelay(2); - } - - if (dev->muted) - outw(STR_WREN, io); - - udelay(4); - outw(omask, io + IO_MASK); - - return data & 0x3ffe; -} - -static void radio_bits_set(struct maestro *dev, u32 data) -{ - u16 io = dev->io, l, bits; - u16 omask, odir; - - omask = inw(io + IO_MASK); - odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); - outw(odir | STR_DATA, io + IO_DIR); - outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); - udelay(16); - for (l = 25; l; l--) { - bits = ((data >> 18) & STR_DATA) | STR_WREN; - data <<= 1; /* shift data */ - outw(bits, io); /* start strobe */ - udelay(2); - outw(bits | STR_CLK, io); /* HI level */ - udelay(2); - outw(bits, io); /* LO level */ - udelay(4); - } - - if (!dev->muted) - outw(0, io); - - udelay(4); - outw(omask, io + IO_MASK); - outw(odir, io + IO_DIR); - msleep(125); -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct maestro *dev = video_drvdata(file); - - strlcpy(v->driver, "radio-maestro", sizeof(v->driver)); - strlcpy(v->card, "Maestro Radio", sizeof(v->card)); - snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev)); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct maestro *dev = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - mutex_lock(&dev->lock); - radio_bits_get(dev); - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (dev->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = dev->tuned; - mutex_unlock(&dev->lock); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maestro *dev = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; - mutex_lock(&dev->lock); - radio_bits_set(dev, FREQ2BITS(f->frequency)); - mutex_unlock(&dev->lock); - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct maestro *dev = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - mutex_lock(&dev->lock); - f->frequency = BITS2FREQ(radio_bits_get(dev)); - mutex_unlock(&dev->lock); - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maestro *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct maestro *dev = video_drvdata(file); - u16 io = dev->io; - u16 omask; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - mutex_lock(&dev->lock); - omask = inw(io + IO_MASK); - outw(~STR_WREN, io + IO_MASK); - dev->muted = ctrl->value; - outw(dev->muted ? STR_WREN : 0, io); - udelay(4); - outw(omask, io + IO_MASK); - msleep(125); - mutex_unlock(&dev->lock); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -static const struct v4l2_file_operations maestro_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops maestro_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; - -static u16 __devinit radio_power_on(struct maestro *dev) -{ - register u16 io = dev->io; - register u32 ofreq; - u16 omask, odir; - - omask = inw(io + IO_MASK); - odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); - outw(odir & ~STR_WREN, io + IO_DIR); - dev->muted = inw(io) & STR_WREN ? 0 : 1; - outw(odir, io + IO_DIR); - outw(~(STR_WREN | STR_CLK), io + IO_MASK); - outw(dev->muted ? 0 : STR_WREN, io); - udelay(16); - outw(omask, io + IO_MASK); - ofreq = radio_bits_get(dev); - - if ((ofreq < FREQ2BITS(FREQ_LO)) || (ofreq > FREQ2BITS(FREQ_HI))) - ofreq = FREQ2BITS(FREQ_LO); - radio_bits_set(dev, ofreq); - - return (ofreq == radio_bits_get(dev)); -} - -static int __devinit maestro_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct maestro *dev; - struct v4l2_device *v4l2_dev; - int retval; - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "enabling pci device failed!\n"); - goto err; - } - - retval = -ENOMEM; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(&pdev->dev, "not enough memory\n"); - goto err; - } - - v4l2_dev = &dev->v4l2_dev; - mutex_init(&dev->lock); - dev->pdev = pdev; - - strlcpy(v4l2_dev->name, "maestro", sizeof(v4l2_dev->name)); - - retval = v4l2_device_register(&pdev->dev, v4l2_dev); - if (retval < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - goto errfr; - } - - dev->io = pci_resource_start(pdev, 0) + GPIO_DATA; - - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.fops = &maestro_fops; - dev->vdev.ioctl_ops = &maestro_ioctl_ops; - dev->vdev.release = video_device_release_empty; - video_set_drvdata(&dev->vdev, dev); - - if (!radio_power_on(dev)) { - retval = -EIO; - goto errfr1; - } - - retval = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr); - if (retval) { - v4l2_err(v4l2_dev, "can't register video device!\n"); - goto errfr1; - } - - v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n"); - - return 0; -errfr1: - v4l2_device_unregister(v4l2_dev); -errfr: - kfree(dev); -err: - return retval; - -} - -static void __devexit maestro_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct maestro *dev = to_maestro(v4l2_dev); - - video_unregister_device(&dev->vdev); - v4l2_device_unregister(&dev->v4l2_dev); -} - -static struct pci_device_id maestro_r_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968), - .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, - .class_mask = 0xffff00 }, - { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978), - .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, - .class_mask = 0xffff00 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl); - -static struct pci_driver maestro_r_driver = { - .name = "maestro_radio", - .id_table = maestro_r_pci_tbl, - .probe = maestro_probe, - .remove = __devexit_p(maestro_remove), -}; - -static int __init maestro_radio_init(void) -{ - int retval = pci_register_driver(&maestro_r_driver); - - if (retval) - printk(KERN_ERR "error during registration pci driver\n"); - - return retval; -} - -static void __exit maestro_radio_exit(void) -{ - pci_unregister_driver(&maestro_r_driver); -} - -module_init(maestro_radio_init); -module_exit(maestro_radio_exit); -- cgit v1.2.3 From 78caf66cb568f2b0c63bf8f87cff2fe1583dd9d0 Mon Sep 17 00:00:00 2001 From: Torsten Schenk Date: Mon, 4 Apr 2011 11:45:28 +0200 Subject: ALSA: 6fire - Update kernel configuration Kernel configuration updated: - experimental dependency removed - description updated Signed-off-by: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/Kconfig | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 97724d8fa9f..cc47a9caec9 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -100,7 +100,6 @@ config SND_USB_US122L config SND_USB_6FIRE tristate "TerraTec DMX 6Fire USB" - depends on EXPERIMENTAL select FW_LOADER select SND_RAWMIDI select SND_PCM @@ -108,11 +107,9 @@ config SND_USB_6FIRE Say Y here to include support for TerraTec 6fire DMX USB interface. You will need firmware files in order to be able to use the device - after it has been coldstarted. This driver currently does not support - firmware loading for all devices. If you own such a device, - you could start windows and let the windows driver upload - the firmware. As long as you do not unplug your device from power, - it should be usable. + after it has been coldstarted. An install script for the firmware + and further help can be found at + http://sixfireusb.sourceforge.net endif # SND_USB -- cgit v1.2.3 From e220fa3bf503d63039fa8e0398a1c252d24663f9 Mon Sep 17 00:00:00 2001 From: Torsten Schenk Date: Mon, 4 Apr 2011 11:47:50 +0200 Subject: ALSA: 6fire - Fix pcm rate assignment Completion of signedness bug for pcm_runtime.rate: variable will never get assigned a negative value now. Signed-off-by: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/6fire/pcm.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba62c7468ba..2110cbf3547 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -456,7 +456,7 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) /* all substreams closed? if so, stop streaming */ if (!rt->playback.instance && !rt->capture.instance) { usb6fire_pcm_stream_stop(rt); - rt->rate = -1; + rt->rate = ARRAY_SIZE(rates); } } mutex_unlock(&rt->stream_mutex); @@ -480,7 +480,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; - int i; int ret; if (rt->panic) @@ -493,12 +492,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) sub->period_off = 0; if (rt->stream_state == STREAM_DISABLED) { - for (i = 0; i < ARRAY_SIZE(rates); i++) - if (alsa_rt->rate == rates[i]) { - rt->rate = i; + for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++) + if (alsa_rt->rate == rates[rt->rate]) break; - } - if (i == ARRAY_SIZE(rates)) { + if (rt->rate == ARRAY_SIZE(rates)) { mutex_unlock(&rt->stream_mutex); snd_printk("invalid rate %d in prepare.\n", alsa_rt->rate); @@ -613,7 +610,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip) rt->chip = chip; rt->stream_state = STREAM_DISABLED; - rt->rate = -1; + rt->rate = ARRAY_SIZE(rates); init_waitqueue_head(&rt->stream_wait_queue); mutex_init(&rt->stream_mutex); -- cgit v1.2.3 From 58c54fa47f5de976959767fa8d9bb857eee4c4e5 Mon Sep 17 00:00:00 2001 From: Torsten Schenk Date: Mon, 4 Apr 2011 11:49:00 +0200 Subject: ALSA: 6fire - Add support for S32_LE format Added support for sample format s32_le. Signed-off-by: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/6fire/pcm.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 2110cbf3547..7ea698793d4 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -64,7 +64,7 @@ static const struct snd_pcm_hardware pcm_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH, - .formats = SNDRV_PCM_FMTBIT_S24_LE, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | @@ -228,7 +228,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) unsigned int total_length = 0; struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; - u32 *src = (u32 *) urb->buffer; + u32 *src = NULL; u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off * (alsa_rt->frame_bits >> 3)); u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size @@ -244,7 +244,12 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) else frame_count = 0; - src = (u32 *) (urb->buffer + total_length); + if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE) + src = (u32 *) (urb->buffer + total_length); + else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE) + src = (u32 *) (urb->buffer - 1 + total_length); + else + return; src++; /* skip leading 4 bytes of every packet */ total_length += urb->packets[i].length; for (frame = 0; frame < frame_count; frame++) { @@ -274,9 +279,18 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub, * (alsa_rt->frame_bits >> 3)); u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size * (alsa_rt->frame_bits >> 3)); - u32 *dest = (u32 *) urb->buffer; + u32 *dest; int bytes_per_frame = alsa_rt->channels << 2; + if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE) + dest = (u32 *) (urb->buffer - 1); + else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE) + dest = (u32 *) (urb->buffer); + else { + snd_printk(KERN_ERR PREFIX "Unknown sample format."); + return; + } + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { /* at least 4 header bytes for valid packet. * after that: 32 bits per sample for analog channels */ -- cgit v1.2.3 From b84610b95f7e7bcd1cd9ecf3e8506a59e9f557fd Mon Sep 17 00:00:00 2001 From: Torsten Schenk Date: Mon, 4 Apr 2011 11:49:57 +0200 Subject: ALSA: 6fire - Improve firmware loader Firmware loader: magical device bytes check updated (accepts all device versions now and accepts possibly loaded firmware, if it is knowing to be working) Signed-off-by: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/6fire/firmware.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 9081a54a9c6..e720035fbd1 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -3,12 +3,6 @@ * * Firmware loader * - * Currently not working for all devices. To be able to use the device - * in linux, it is also possible to let the windows driver upload the firmware. - * For that, start the computer in windows and reboot. - * As long as the device is connected to the power supply, no firmware reload - * needs to be performed. - * * Author: Torsten Schenk * Created: Jan 01, 2011 * Version: 0.3.0 @@ -72,6 +66,10 @@ static const u8 ep_w_max_packet_size[] = { 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */ }; +static const u8 known_fw_versions[][4] = { + { 0x03, 0x01, 0x0b, 0x00 } +}; + struct ihex_record { u16 address; u8 len; @@ -363,6 +361,25 @@ static int usb6fire_fw_fpga_upload( return 0; } +/* check, if the firmware version the devices has currently loaded + * is known by this driver. 'version' needs to have 4 bytes version + * info data. */ +static int usb6fire_fw_check(u8 *version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++) + if (!memcmp(version, known_fw_versions + i, 4)) + return 0; + + snd_printk(KERN_ERR PREFIX "invalid fimware version in device: " + "%02x %02x %02x %02x. " + "please reconnect to power. if this failure " + "still happens, check your firmware installation.", + version[0], version[1], version[2], version[3]); + return -EINVAL; +} + int usb6fire_fw_init(struct usb_interface *intf) { int i; @@ -378,9 +395,7 @@ int usb6fire_fw_init(struct usb_interface *intf) "firmware state.\n"); return ret; } - if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55 - || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7] - != 0x00) { + if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) { snd_printk(KERN_ERR PREFIX "unknown device firmware state " "received from device: "); for (i = 0; i < 8; i++) @@ -389,7 +404,7 @@ int usb6fire_fw_init(struct usb_interface *intf) return -EIO; } /* do we need fpga loader ezusb firmware? */ - if (buffer[3] == 0x01 && buffer[6] == 0x19) { + if (buffer[3] == 0x01) { ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6firel2.ihx", 0, NULL, 0); if (ret < 0) @@ -397,7 +412,10 @@ int usb6fire_fw_init(struct usb_interface *intf) return FW_NOT_READY; } /* do we need fpga firmware and application ezusb firmware? */ - else if (buffer[3] == 0x02 && buffer[6] == 0x0b) { + else if (buffer[3] == 0x02) { + ret = usb6fire_fw_check(buffer + 4); + if (ret < 0) + return ret; ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin"); if (ret < 0) return ret; @@ -410,8 +428,8 @@ int usb6fire_fw_init(struct usb_interface *intf) return FW_NOT_READY; } /* all fw loaded? */ - else if (buffer[3] == 0x03 && buffer[6] == 0x0b) - return 0; + else if (buffer[3] == 0x03) + return usb6fire_fw_check(buffer + 4); /* unknown data? */ else { snd_printk(KERN_ERR PREFIX "unknown device firmware state " -- cgit v1.2.3 From 2475b0d407614ea5a41b8325d45c614d94087088 Mon Sep 17 00:00:00 2001 From: Torsten Schenk Date: Mon, 4 Apr 2011 11:50:53 +0200 Subject: ALSA: 6fire - Add support of digital-thru mixer Digital Thru mixer element added (device can act as converter optical<->coax) Signed-off-by: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/6fire/control.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/6fire/control.h | 17 ++++++++ sound/usb/6fire/pcm.c | 62 +++++++-------------------- 3 files changed, 137 insertions(+), 47 deletions(-) diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 24846351118..ac828eff1a6 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -65,6 +65,15 @@ init_data[] = { { 0 } /* TERMINATING ENTRY */ }; +static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; +/* values to write to soundcard register for all samplerates */ +static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; +static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; + +enum { + DIGITAL_THRU_ONLY_SAMPLERATE = 3 +}; + static void usb6fire_control_master_vol_update(struct control_runtime *rt) { struct comm_runtime *comm_rt = rt->chip->comm; @@ -95,6 +104,67 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt) } } +static int usb6fire_control_set_rate(struct control_runtime *rt, int rate) +{ + int ret; + struct usb_device *device = rt->chip->dev; + struct comm_runtime *comm_rt = rt->chip->comm; + + if (rate < 0 || rate >= CONTROL_N_RATES) + return -EINVAL; + + ret = usb_set_interface(device, 1, rates_altsetting[rate]); + if (ret < 0) + return ret; + + /* set soundcard clock */ + ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], + rates_6fire_vh[rate]); + if (ret < 0) + return ret; + + return 0; +} + +static int usb6fire_control_set_channels( + struct control_runtime *rt, int n_analog_out, + int n_analog_in, bool spdif_out, bool spdif_in) +{ + int ret; + struct comm_runtime *comm_rt = rt->chip->comm; + + /* enable analog inputs and outputs + * (one bit per stereo-channel) */ + ret = comm_rt->write16(comm_rt, 0x02, 0x02, + (1 << (n_analog_out / 2)) - 1, + (1 << (n_analog_in / 2)) - 1); + if (ret < 0) + return ret; + + /* disable digital inputs and outputs */ + /* TODO: use spdif_x to enable/disable digital channels */ + ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); + if (ret < 0) + return ret; + + return 0; +} + +static int usb6fire_control_streaming_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + + if (comm_rt) { + if (!rt->usb_streaming && rt->digital_thru_switch) + usb6fire_control_set_rate(rt, + DIGITAL_THRU_ONLY_SAMPLERATE); + return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, + (rt->usb_streaming ? 0x01 : 0x00) | + (rt->digital_thru_switch ? 0x08 : 0x00)); + } + return -EINVAL; +} + static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, return 0; } +static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { + rt->digital_thru_switch = ucontrol->value.integer.value[0]; + usb6fire_control_streaming_update(rt); + changed = 1; + } + return changed; +} + +static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = rt->digital_thru_switch; + return 0; +} + static struct __devinitdata snd_kcontrol_new elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = { .get = usb6fire_control_opt_coax_get, .put = usb6fire_control_opt_coax_put }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Thru Playback Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = usb6fire_control_digital_thru_get, + .put = usb6fire_control_digital_thru_put + }, {} }; @@ -238,6 +339,9 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) return -ENOMEM; rt->chip = chip; + rt->update_streaming = usb6fire_control_streaming_update; + rt->set_rate = usb6fire_control_set_rate; + rt->set_channels = usb6fire_control_set_channels; i = 0; while (init_data[i].type) { @@ -249,6 +353,7 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip) usb6fire_control_opt_coax_update(rt); usb6fire_control_line_phono_update(rt); usb6fire_control_master_vol_update(rt); + usb6fire_control_streaming_update(rt); i = 0; while (elements[i].name) { diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index b534c777ab0..8f5aeead2e3 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h @@ -21,12 +21,29 @@ enum { CONTROL_MAX_ELEMENTS = 32 }; +enum { + CONTROL_RATE_44KHZ, + CONTROL_RATE_48KHZ, + CONTROL_RATE_88KHZ, + CONTROL_RATE_96KHZ, + CONTROL_RATE_176KHZ, + CONTROL_RATE_192KHZ, + CONTROL_N_RATES +}; + struct control_runtime { + int (*update_streaming)(struct control_runtime *rt); + int (*set_rate)(struct control_runtime *rt, int rate); + int (*set_channels)(struct control_runtime *rt, int n_analog_out, + int n_analog_in, bool spdif_out, bool spdif_in); + struct sfire_chip *chip; struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS]; bool opt_coax_switch; bool line_phono_switch; + bool digital_thru_switch; + bool usb_streaming; u8 master_vol; }; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 7ea698793d4..b137b25865c 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -17,26 +17,23 @@ #include "pcm.h" #include "chip.h" #include "comm.h" +#include "control.h" enum { OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4 }; /* keep next two synced with - * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */ + * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE + * and CONTROL_RATE_XXX in control.h */ static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 }; static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 }; static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; -static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; static const int rates_alsaid[] = { SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000, SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000, SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 }; -/* values to write to soundcard register for all samplerates */ -static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; -static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; - enum { /* settings for pcm */ OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024 }; @@ -48,15 +45,6 @@ enum { /* pcm streaming states */ STREAM_STOPPING }; -enum { /* pcm sample rates (also index into RATES_XXX[]) */ - RATE_44KHZ, - RATE_48KHZ, - RATE_88KHZ, - RATE_96KHZ, - RATE_176KHZ, - RATE_192KHZ -}; - static const struct snd_pcm_hardware pcm_hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | @@ -87,57 +75,34 @@ static const struct snd_pcm_hardware pcm_hw = { static int usb6fire_pcm_set_rate(struct pcm_runtime *rt) { int ret; - struct usb_device *device = rt->chip->dev; - struct comm_runtime *comm_rt = rt->chip->comm; + struct control_runtime *ctrl_rt = rt->chip->control; - if (rt->rate >= ARRAY_SIZE(rates)) - return -EINVAL; - /* disable streaming */ - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); + ctrl_rt->usb_streaming = false; + ret = ctrl_rt->update_streaming(ctrl_rt); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error stopping streaming while " "setting samplerate %d.\n", rates[rt->rate]); return ret; } - ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error setting interface " - "altsetting %d for samplerate %d.\n", - rates_altsetting[rt->rate], rates[rt->rate]); - return ret; - } - - /* set soundcard clock */ - ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate], - rates_6fire_vh[rt->rate]); + ret = ctrl_rt->set_rate(ctrl_rt, rt->rate); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n", rates[rt->rate]); return ret; } - /* enable analog inputs and outputs - * (one bit per stereo-channel) */ - ret = comm_rt->write16(comm_rt, 0x02, 0x02, - (1 << (OUT_N_CHANNELS / 2)) - 1, - (1 << (IN_N_CHANNELS / 2)) - 1); + ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS, + false, false); if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error initializing analog channels " + snd_printk(KERN_ERR PREFIX "error initializing channels " "while setting samplerate %d.\n", rates[rt->rate]); return ret; } - /* disable digital inputs and outputs */ - ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); - if (ret < 0) { - snd_printk(KERN_ERR PREFIX "error initializing digital " - "channels while setting samplerate %d.\n", - rates[rt->rate]); - return ret; - } - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01); + ctrl_rt->usb_streaming = true; + ret = ctrl_rt->update_streaming(ctrl_rt); if (ret < 0) { snd_printk(KERN_ERR PREFIX "error starting streaming while " "setting samplerate %d.\n", rates[rt->rate]); @@ -168,12 +133,15 @@ static struct pcm_substream *usb6fire_pcm_get_substream( static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) { int i; + struct control_runtime *ctrl_rt = rt->chip->control; if (rt->stream_state != STREAM_DISABLED) { for (i = 0; i < PCM_N_URBS; i++) { usb_kill_urb(&rt->in_urbs[i].instance); usb_kill_urb(&rt->out_urbs[i].instance); } + ctrl_rt->usb_streaming = false; + ctrl_rt->update_streaming(ctrl_rt); rt->stream_state = STREAM_DISABLED; } } -- cgit v1.2.3 From d8e4f9aed82c68b60f81b2e7977c46868d51062f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Apr 2011 12:43:23 +0200 Subject: ALSA: core - Don't use "default' for default The card-id parser assigns the string "default" when no appropriate word is found in the card name. But this string may confuse the alsa-lib, so better to avoid. Use "Default" now instead. Signed-off-by: Takashi Iwai --- sound/core/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/init.c b/sound/core/init.c index a0080aa45ae..30ecad41403 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -514,7 +514,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) id = card->id; if (*id == '\0') - strcpy(id, "default"); + strcpy(id, "Default"); while (1) { if (loops-- == 0) { -- cgit v1.2.3 From 550ac6ba4ef0e57f6edbadf2b018d5125d2f7e98 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:41 +1200 Subject: ALSA: snd-asihpi: Control naming Clock source is neither capture nor playback, so change 'Capture Clock' to 'Clock'. Add spaces to control name string for consistency, always 'PCM 0' , never 'PCM0' Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index f53a31e939c..edcbe39d603 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1413,14 +1413,16 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, struct hpi_control *hpi_ctl, char *name) { - char *dir = ""; + char *dir; memset(snd_control, 0, sizeof(*snd_control)); snd_control->name = hpi_ctl->name; snd_control->private_value = hpi_ctl->h_control; snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; snd_control->index = 0; - if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) + if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE) + dir = ""; /* clock is neither capture nor playback */ + else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) dir = "Capture "; /* On or towards a PCM capture destination*/ else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && (!hpi_ctl->dst_node_type)) @@ -1433,7 +1435,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, dir = "Playback "; /* PCM Playback source, or output node */ if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) - sprintf(hpi_ctl->name, "%s%d %s%d %s%s", + sprintf(hpi_ctl->name, "%s %d %s %d %s%s", asihpi_src_names[hpi_ctl->src_node_type], hpi_ctl->src_node_index, asihpi_dst_names[hpi_ctl->dst_node_type], -- cgit v1.2.3 From a6477134db119a22aa30911ff18e440b8db9df65 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:42 +1200 Subject: ALSA: asihpi: Update debug printing Debug print full substream ID. Other minor debug print updates. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 102 +++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index edcbe39d603..434342f874f 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -57,8 +57,25 @@ MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); */ #define snd_printddd(format, args...) \ __snd_printk(3, __FILE__, __LINE__, format, ##args) + +/* copied from pcm_lib.c, hope later patch will make that version public +and this copy can be removed */ +static void pcm_debug_name(struct snd_pcm_substream *substream, + char *name, size_t len) +{ + snprintf(name, len, "pcmC%dD%d%c:%d", + substream->pcm->card->number, + substream->pcm->device, + substream->stream ? 'c' : 'p', + substream->number); +} +#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name)) + #else -#define snd_printddd(format, args...) do { } while (0) +#define snd_printddd(format, args...) do { } while (0) +#define pcm_debug_name(s, n, l) do { } while (0) +#define DEBUG_NAME(name, substream) do { } while (0) + #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ @@ -101,13 +118,6 @@ static int adapter_fs = DEFAULT_SAMPLERATE; #define PERIOD_BYTES_MIN 2048 #define BUFFER_BYTES_MAX (512 * 1024) -/* convert stream to character */ -#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C') - -/*#define TIMER_MILLISECONDS 20 -#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000) -*/ - #define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7) struct clk_source { @@ -288,19 +298,26 @@ static u16 handle_error(u16 err, int line, char *filename) #define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__) /***************************** GENERAL PCM ****************/ -static void print_hwparams(struct snd_pcm_hw_params *p) + +static void print_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *p) { - snd_printd("HWPARAMS \n"); - snd_printd("samplerate %d \n", params_rate(p)); - snd_printd("Channels %d \n", params_channels(p)); - snd_printd("Format %d \n", params_format(p)); - snd_printd("subformat %d \n", params_subformat(p)); - snd_printd("Buffer bytes %d \n", params_buffer_bytes(p)); - snd_printd("Period bytes %d \n", params_period_bytes(p)); - snd_printd("access %d \n", params_access(p)); - snd_printd("period_size %d \n", params_period_size(p)); - snd_printd("periods %d \n", params_periods(p)); - snd_printd("buffer_size %d \n", params_buffer_size(p)); + DEBUG_NAME(substream, name); + snd_printd("%s HWPARAMS\n", name); + snd_printd(" samplerate %d Hz\n", params_rate(p)); + snd_printd(" channels %d\n", params_channels(p)); + snd_printd(" format %d\n", params_format(p)); + snd_printd(" subformat %d\n", params_subformat(p)); + snd_printd(" buffer %d B\n", params_buffer_bytes(p)); + snd_printd(" period %d B\n", params_period_bytes(p)); + snd_printd(" access %d\n", params_access(p)); + snd_printd(" period_size %d\n", params_period_size(p)); + snd_printd(" periods %d\n", params_periods(p)); + snd_printd(" buffer_size %d\n", params_buffer_size(p)); + snd_printd(" %d B/s\n", params_rate(p) * + params_channels(p) * + snd_pcm_format_width(params_format(p)) / 8); + } static snd_pcm_format_t hpi_to_alsa_formats[] = { @@ -451,7 +468,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, int width; unsigned int bytes_per_sec; - print_hwparams(params); + print_hwparams(substream, params); err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); if (err < 0) return err; @@ -459,10 +476,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, if (err) return err; - snd_printdd("format %d, %d chans, %d_hz\n", - format, params_channels(params), - params_rate(params)); - hpi_handle_error(hpi_format_create(&dpcm->format, params_channels(params), format, params_rate(params), 0, 0)); @@ -509,8 +522,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, dpcm->bytes_per_sec = bytes_per_sec; dpcm->buffer_bytes = params_buffer_bytes(params); dpcm->period_bytes = params_period_bytes(params); - snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n", - dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec); return 0; } @@ -564,9 +575,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; u16 e; + DEBUG_NAME(substream, name); + + snd_printdd("%s trigger\n", name); - snd_printdd("%c%d trigger\n", - SCHR(substream->stream), substream->number); switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_pcm_group_for_each_entry(s, substream) { @@ -599,9 +611,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } if (card->support_grouping) { - snd_printdd("\t%c%d group\n", - SCHR(s->stream), - s->number); + snd_printdd("%d group\n", s->number); e = hpi_stream_group_add( dpcm->h_stream, ds->h_stream); @@ -636,9 +646,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, s->runtime->status->state = SNDRV_PCM_STATE_SETUP; if (card->support_grouping) { - snd_printdd("\t%c%d group\n", - SCHR(s->stream), - s->number); + snd_printdd("%d group\n", s->number); snd_pcm_trigger_done(s, substream); } else break; @@ -732,9 +740,9 @@ static void snd_card_asihpi_timer_function(unsigned long data) int loops = 0; u16 state; u32 buffer_size, bytes_avail, samples_played, on_card_bytes; + DEBUG_NAME(substream, name); - snd_printdd("%c%d snd_card_asihpi_timer_function\n", - SCHR(substream->stream), substream->number); + snd_printdd("%s snd_card_asihpi_timer_function\n", name); /* find minimum newdata and buffer pos in group */ snd_pcm_group_for_each_entry(s, substream) { @@ -786,16 +794,18 @@ static void snd_card_asihpi_timer_function(unsigned long data) newdata); } - snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n", + snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n", (unsigned long)frames_to_bytes(runtime, runtime->status->hw_ptr), (unsigned long)frames_to_bytes(runtime, runtime->control->appl_ptr)); - snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X," - " aux=x%04X space=x%04X\n", - loops, SCHR(s->stream), s->number, - state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail, + snd_printdd("%d S=%d, " + "rw=0x%04X, dma=0x%04X, left=0x%04X, " + "aux=0x%04X space=0x%04X\n", + s->number, state, + ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, + (int)bytes_avail, (int)on_card_bytes, buffer_size-bytes_avail); loops++; } @@ -814,7 +824,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) next_jiffies = max(next_jiffies, 1U); dpcm->timer.expires = jiffies + next_jiffies; - snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n", + snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n", next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); snd_pcm_group_for_each_entry(s, substream) { @@ -863,7 +873,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { - snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd); + snd_printddd(KERN_INFO "P%d ioctl %d\n", substream->number, cmd); return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -873,7 +883,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - snd_printdd("playback prepare %d\n", substream->number); + snd_printdd("P%d prepare\n", substream->number); hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); dpcm->pcm_buf_host_rw_ofs = 0; @@ -890,7 +900,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t ptr; ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); - snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr); + snd_printddd("P%d pointer = 0x%04lx\n", substream->number, (unsigned long)ptr); return ptr; } -- cgit v1.2.3 From 0b7ce9e2bd2d9dbc8f4797b0cd5e0d138cb529e1 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:43 +1200 Subject: ALSA: asihpi: Handle playback drained status better Use the card drained status reporting for playback, but allow it to persist for a few timer cycles before signalling XRUN, to allow card to recover by itself. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 434342f874f..a5226e3af3d 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -165,6 +165,7 @@ struct snd_card_asihpi_pcm { unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */ unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */ unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */ + unsigned int drained_count; struct snd_pcm_substream *substream; u32 h_stream; struct hpi_format format; @@ -592,6 +593,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, if (substream->stream != s->stream) continue; + ds->drained_count = 0; if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) && (card->support_mmap)) { /* How do I know how much valid data is present @@ -771,12 +773,18 @@ static void snd_card_asihpi_timer_function(unsigned long data) (on_card_bytes < ds->pcm_buf_host_rw_ofs)) { hpi_handle_error(hpi_stream_start(ds->h_stream)); snd_printdd("P%d start\n", s->number); + ds->drained_count = 0; } } else if (state == HPI_STATE_DRAINED) { snd_printd(KERN_WARNING "P%d drained\n", s->number); - /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); - continue; */ + ds->drained_count++; + if (ds->drained_count > 2) { + snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); + continue; + } + } else { + ds->drained_count = 0; } } else pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs; -- cgit v1.2.3 From f3d145aac913b318e96e5c2763d8908805a5e30a Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:44 +1200 Subject: ALSA: asihpi: MMAP for non-busmaster cards Allow older non DMA capable cards to use MMAP by emulating the DMA using read and write functions, and getting rid of copy & silence callbacks that were used only by older cards. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 205 ++++++++++++++++------------------------------ 1 file changed, 70 insertions(+), 135 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index a5226e3af3d..d5258aa738a 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -42,6 +42,7 @@ #include #include + MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. "); MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); @@ -146,7 +147,7 @@ struct snd_card_asihpi { u32 h_mixer; struct clk_cache cc; - u16 support_mmap; + u16 can_dma; u16 support_grouping; u16 support_mrx; u16 update_interval_frames; @@ -491,8 +492,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, } dpcm->hpi_buffer_attached = 0; - if (card->support_mmap) { - + if (card->can_dma) { err = hpi_stream_host_buffer_attach(dpcm->h_stream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { @@ -594,8 +594,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, continue; ds->drained_count = 0; - if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) && - (card->support_mmap)) { + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* How do I know how much valid data is present * in buffer? Must be at least one period! * Guessing 2 periods, but if @@ -630,7 +629,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, /* start the master stream */ snd_card_asihpi_pcm_timer_start(substream); if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || - !card->support_mmap) + !card->can_dma) hpi_handle_error(hpi_stream_start(dpcm->h_stream)); break; @@ -766,6 +765,9 @@ static void snd_card_asihpi_timer_function(unsigned long data) /* number of bytes in on-card buffer */ runtime->delay = on_card_bytes; + if (!card->can_dma) + on_card_bytes = bytes_avail; + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail; if (state == HPI_STATE_STOPPED) { @@ -844,30 +846,63 @@ static void snd_card_asihpi_timer_function(unsigned long data) ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; - if (xfercount && (on_card_bytes <= ds->period_bytes)) { - if (card->support_mmap) { - if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - snd_printddd("P%d write x%04x\n", + if (xfercount && + /* Limit use of on card fifo for playback */ + ((on_card_bytes <= ds->period_bytes) || + (s->stream == SNDRV_PCM_STREAM_CAPTURE))) + + { + + unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes; + unsigned int xfer1, xfer2; + char *pd = &s->runtime->dma_area[buf_ofs]; + + if (card->can_dma) { + xfer1 = xfercount; + xfer2 = 0; + } else { + xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs); + xfer2 = xfercount - xfer1; + } + + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_printddd("P%d write1 0x%04X 0x%04X\n", + s->number, xfer1, buf_ofs); + hpi_handle_error( + hpi_outstream_write_buf( + ds->h_stream, pd, xfer1, + &ds->format)); + + if (xfer2) { + pd = s->runtime->dma_area; + + snd_printddd("P%d write2 0x%04X 0x%04X\n", s->number, - ds->period_bytes); + xfercount - xfer1, buf_ofs); hpi_handle_error( hpi_outstream_write_buf( - ds->h_stream, - &s->runtime-> - dma_area[0], - xfercount, + ds->h_stream, pd, + xfercount - xfer1, &ds->format)); - } else { - snd_printddd("C%d read x%04x\n", - s->number, - xfercount); + } + } else { + snd_printddd("C%d read1 0x%04x\n", + s->number, xfer1); + hpi_handle_error( + hpi_instream_read_buf( + ds->h_stream, + pd, xfer1)); + if (xfer2) { + pd = s->runtime->dma_area; + snd_printddd("C%d read2 0x%04x\n", + s->number, xfer2); hpi_handle_error( hpi_instream_read_buf( ds->h_stream, - NULL, xfercount)); + pd, xfer2)); } - ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount; - } /* else R/W will be handled by read/write callbacks */ + } + ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount; ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs; snd_pcm_period_elapsed(s); } @@ -1004,11 +1039,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) SNDRV_PCM_INFO_DOUBLE | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_PAUSE; - - if (card->support_mmap) - snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; if (card->support_grouping) snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; @@ -1016,7 +1049,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) /* struct is copied, so can create initializer dynamically */ runtime->hw = snd_card_asihpi_playback; - if (card->support_mmap) + if (card->can_dma) err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES); if (err < 0) @@ -1046,58 +1079,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) return 0; } -static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream, - int channel, - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - unsigned int len; - - len = frames_to_bytes(runtime, count); - - if (copy_from_user(runtime->dma_area, src, len)) - return -EFAULT; - - snd_printddd("playback copy%d %u bytes\n", - substream->number, len); - - hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, - runtime->dma_area, len, &dpcm->format)); - - dpcm->pcm_buf_host_rw_ofs += len; - - return 0; -} - -static int snd_card_asihpi_playback_silence(struct snd_pcm_substream * - substream, int channel, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) -{ - /* Usually writes silence to DMA buffer, which should be overwritten - by real audio later. Our fifos cannot be overwritten, and are not - free-running DMAs. Silence is output on fifo underflow. - This callback is still required to allow the copy callback to be used. - */ - return 0; -} - -static struct snd_pcm_ops snd_card_asihpi_playback_ops = { - .open = snd_card_asihpi_playback_open, - .close = snd_card_asihpi_playback_close, - .ioctl = snd_card_asihpi_playback_ioctl, - .hw_params = snd_card_asihpi_pcm_hw_params, - .hw_free = snd_card_asihpi_hw_free, - .prepare = snd_card_asihpi_playback_prepare, - .trigger = snd_card_asihpi_trigger, - .pointer = snd_card_asihpi_playback_pointer, - .copy = snd_card_asihpi_playback_copy, - .silence = snd_card_asihpi_playback_silence, -}; - static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { .open = snd_card_asihpi_playback_open, .close = snd_card_asihpi_playback_close, @@ -1229,18 +1210,16 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) snd_card_asihpi_capture_format(card, dpcm->h_stream, &snd_card_asihpi_capture); snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture); - snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED; - - if (card->support_mmap) - snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; + snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; if (card->support_grouping) snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START; runtime->hw = snd_card_asihpi_capture; - if (card->support_mmap) + if (card->can_dma) err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES); if (err < 0) @@ -1264,28 +1243,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) return 0; } -static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - u32 len; - - len = frames_to_bytes(runtime, count); - - snd_printddd("capture copy%d %d bytes\n", substream->number, len); - hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream, - runtime->dma_area, len)); - - dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len; - - if (copy_to_user(dst, runtime->dma_area, len)) - return -EFAULT; - - return 0; -} - static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { .open = snd_card_asihpi_capture_open, .close = snd_card_asihpi_capture_close, @@ -1297,18 +1254,6 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { .pointer = snd_card_asihpi_capture_pointer, }; -static struct snd_pcm_ops snd_card_asihpi_capture_ops = { - .open = snd_card_asihpi_capture_open, - .close = snd_card_asihpi_capture_close, - .ioctl = snd_card_asihpi_capture_ioctl, - .hw_params = snd_card_asihpi_pcm_hw_params, - .hw_free = snd_card_asihpi_hw_free, - .prepare = snd_card_asihpi_capture_prepare, - .trigger = snd_card_asihpi_trigger, - .pointer = snd_card_asihpi_capture_pointer, - .copy = snd_card_asihpi_capture_copy -}; - static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device, int substreams) { @@ -1321,17 +1266,10 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, if (err < 0) return err; /* pointer to ops struct is stored, dont change ops afterwards! */ - if (asihpi->support_mmap) { snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_asihpi_playback_mmap_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_asihpi_capture_mmap_ops); - } else { - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_card_asihpi_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_card_asihpi_capture_ops); - } pcm->private_data = asihpi; pcm->info_flags = 0; @@ -2895,14 +2833,14 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, if (err) asihpi->update_interval_frames = 512; - if (!asihpi->support_mmap) + if (!asihpi->can_dma) asihpi->update_interval_frames *= 2; hpi_handle_error(hpi_instream_open(asihpi->adapter_index, 0, &h_stream)); err = hpi_instream_host_buffer_free(h_stream); - asihpi->support_mmap = (!err); + asihpi->can_dma = (!err); hpi_handle_error(hpi_instream_close(h_stream)); @@ -2914,8 +2852,8 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->out_max_chans = 2; } - snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n", - asihpi->support_mmap, + snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n", + asihpi->can_dma, asihpi->support_grouping, asihpi->support_mrx ); @@ -2945,10 +2883,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, by enable_hwdep module param*/ snd_asihpi_hpi_new(asihpi, 0, NULL); - if (asihpi->support_mmap) - strcpy(card->driver, "ASIHPI-MMAP"); - else - strcpy(card->driver, "ASIHPI"); + strcpy(card->driver, "ASIHPI"); sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type); sprintf(card->longname, "%s %i", -- cgit v1.2.3 From 6027dfa46ea477b1223d83e74a3b3dcc572285b5 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:45 +1200 Subject: ALSA: asihpi: Remove 2 unused functions Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpifunc.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index c38fc948756..7397b169b89 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -105,33 +105,6 @@ u16 hpi_subsys_get_version_ex(u32 *pversion_ex) return hr.error; } -u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource, - u16 *pw_adapter_index) -{ - struct hpi_message hm; - struct hpi_response hr; - - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_CREATE_ADAPTER); - hm.u.s.resource = *p_resource; - - hpi_send_recv(&hm, &hr); - - *pw_adapter_index = hr.u.s.adapter_index; - return hr.error; -} - -u16 hpi_subsys_delete_adapter(u16 adapter_index) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_DELETE_ADAPTER); - hm.obj_index = adapter_index; - hpi_send_recv(&hm, &hr); - return hr.error; -} - u16 hpi_subsys_get_num_adapters(int *pn_num_adapters) { struct hpi_message hm; -- cgit v1.2.3 From b0096a65677fa8d7e50975dc7282ce313610d9e8 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:46 +1200 Subject: ALSA: asihpi: Standardise substream name generation Define and use pcm_debug_name if CONFIG_SND_DEBUG Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index d5258aa738a..eac7b08a161 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -47,18 +47,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. "); MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); -#if defined CONFIG_SND_DEBUG_VERBOSE -/** - * snd_printddd - very verbose debug printk - * @format: format string - * - * Works like snd_printk() for debugging purposes. - * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. - * Must set snd module debug parameter to 3 to enable at runtime. - */ -#define snd_printddd(format, args...) \ - __snd_printk(3, __FILE__, __LINE__, format, ##args) - +#if defined CONFIG_SND_DEBUG /* copied from pcm_lib.c, hope later patch will make that version public and this copy can be removed */ static void pcm_debug_name(struct snd_pcm_substream *substream, @@ -71,12 +60,24 @@ static void pcm_debug_name(struct snd_pcm_substream *substream, substream->number); } #define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name)) - #else -#define snd_printddd(format, args...) do { } while (0) #define pcm_debug_name(s, n, l) do { } while (0) #define DEBUG_NAME(name, substream) do { } while (0) +#endif +#if defined CONFIG_SND_DEBUG_VERBOSE +/** + * snd_printddd - very verbose debug printk + * @format: format string + * + * Works like snd_printk() for debugging purposes. + * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. + * Must set snd module debug parameter to 3 to enable at runtime. + */ +#define snd_printddd(format, args...) \ + __snd_printk(3, __FILE__, __LINE__, format, ##args) +#else +#define snd_printddd(format, args...) do { } while (0) #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ @@ -857,7 +858,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) unsigned int xfer1, xfer2; char *pd = &s->runtime->dma_area[buf_ofs]; - if (card->can_dma) { + if (card->can_dma) { /* buffer wrap is handled at lower level */ xfer1 = xfercount; xfer2 = 0; } else { -- cgit v1.2.3 From 6d0b898e9c402d6b7d0d07adacdbee2ebedafdcd Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:47 +1200 Subject: ALSA: asihpi: Simplify driver unload cleanup Replacing subsys_delete_adapter with adapter_delete allows some special-case adapter lookup code to be removed. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpi6000.c | 39 +++++++---------- sound/pci/asihpi/hpi6205.c | 97 +++++++++++++++++++++-------------------- sound/pci/asihpi/hpi_internal.h | 17 ++++---- sound/pci/asihpi/hpicmn.c | 1 - sound/pci/asihpi/hpimsgx.c | 31 ++++++------- sound/pci/asihpi/hpioctl.c | 54 ++++++++++++----------- 6 files changed, 113 insertions(+), 126 deletions(-) diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 3e3c2ef6efd..09befdafe09 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -200,8 +200,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata, static void subsys_create_adapter(struct hpi_message *phm, struct hpi_response *phr); -static void subsys_delete_adapter(struct hpi_message *phm, - struct hpi_response *phr); +static void adapter_delete(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); static void adapter_get_asserts(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr); @@ -222,9 +222,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) case HPI_SUBSYS_CREATE_ADAPTER: subsys_create_adapter(phm, phr); break; - case HPI_SUBSYS_DELETE_ADAPTER: - subsys_delete_adapter(phm, phr); - break; default: phr->error = HPI_ERROR_INVALID_FUNC; break; @@ -279,6 +276,10 @@ static void adapter_message(struct hpi_adapter_obj *pao, adapter_get_asserts(pao, phm, phr); break; + case HPI_ADAPTER_DELETE: + adapter_delete(pao, phm, phr); + break; + default: hw_message(pao, phm, phr); break; @@ -333,26 +334,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr) { struct hpi_adapter_obj *pao = NULL; - /* subsytem messages get executed by every HPI. */ - /* All other messages are ignored unless the adapter index matches */ - /* an adapter in the HPI */ - /*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */ - - /* if Dsp has crashed then do not communicate with it any more */ if (phm->object != HPI_OBJ_SUBSYSTEM) { pao = hpi_find_adapter(phm->adapter_index); if (!pao) { - HPI_DEBUG_LOG(DEBUG, - " %d,%d refused, for another HPI?\n", - phm->object, phm->function); + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_BAD_ADAPTER_NUMBER); + HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n", + phm->adapter_index); return; } + /* Don't even try to communicate with crashed DSP */ if (pao->dsp_crashed >= 10) { hpi_init_response(phr, phm->object, phm->function, HPI_ERROR_DSP_HARDWARE); - HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n", - phm->object, phm->function); + HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n", + phm->adapter_index); return; } } @@ -463,15 +460,9 @@ static void subsys_create_adapter(struct hpi_message *phm, phr->error = 0; } -static void subsys_delete_adapter(struct hpi_message *phm, - struct hpi_response *phr) +static void adapter_delete(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) { - struct hpi_adapter_obj *pao = NULL; - - pao = hpi_find_adapter(phm->obj_index); - if (!pao) - return; - delete_adapter_obj(pao); hpi_delete_adapter(pao); phr->error = 0; diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 620525bdac5..53037342cd2 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -152,8 +152,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, static void subsys_create_adapter(struct hpi_message *phm, struct hpi_response *phr); -static void subsys_delete_adapter(struct hpi_message *phm, - struct hpi_response *phr); +static void adapter_delete(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); static u16 create_adapter_obj(struct hpi_adapter_obj *pao, u32 *pos_error_code); @@ -223,15 +223,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index); /*****************************************************************************/ -static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) +static void subsys_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) { switch (phm->function) { case HPI_SUBSYS_CREATE_ADAPTER: subsys_create_adapter(phm, phr); break; - case HPI_SUBSYS_DELETE_ADAPTER: - subsys_delete_adapter(phm, phr); - break; default: phr->error = HPI_ERROR_INVALID_FUNC; break; @@ -279,6 +277,10 @@ static void adapter_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr) { switch (phm->function) { + case HPI_ADAPTER_DELETE: + adapter_delete(pao, phm, phr); + break; + default: hw_message(pao, phm, phr); break; @@ -371,36 +373,17 @@ static void instream_message(struct hpi_adapter_obj *pao, /** Entry point to this HPI backend * All calls to the HPI start here */ -void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) +void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm, + struct hpi_response *phr) { - struct hpi_adapter_obj *pao = NULL; - - /* subsytem messages are processed by every HPI. - * All other messages are ignored unless the adapter index matches - * an adapter in the HPI - */ - /* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject, - phm->wFunction); */ - - /* if Dsp has crashed then do not communicate with it any more */ - if (phm->object != HPI_OBJ_SUBSYSTEM) { - pao = hpi_find_adapter(phm->adapter_index); - if (!pao) { - HPI_DEBUG_LOG(DEBUG, - " %d,%d refused, for another HPI?\n", - phm->object, phm->function); - return; - } - - if ((pao->dsp_crashed >= 10) - && (phm->function != HPI_ADAPTER_DEBUG_READ)) { - /* allow last resort debug read even after crash */ - hpi_init_response(phr, phm->object, phm->function, - HPI_ERROR_DSP_HARDWARE); - HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", - phm->object, phm->function); - return; - } + if (pao && (pao->dsp_crashed >= 10) + && (phm->function != HPI_ADAPTER_DEBUG_READ)) { + /* allow last resort debug read even after crash */ + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_DSP_HARDWARE); + HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object, + phm->function); + return; } /* Init default response */ @@ -412,7 +395,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) case HPI_TYPE_MESSAGE: switch (phm->object) { case HPI_OBJ_SUBSYSTEM: - subsys_message(phm, phr); + subsys_message(pao, phm, phr); break; case HPI_OBJ_ADAPTER: @@ -444,6 +427,26 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) } } +void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_adapter_obj *pao = NULL; + + if (phm->object != HPI_OBJ_SUBSYSTEM) { + /* normal messages must have valid adapter index */ + pao = hpi_find_adapter(phm->adapter_index); + } else { + /* subsys messages don't address an adapter */ + _HPI_6205(NULL, phm, phr); + return; + } + + if (pao) + _HPI_6205(pao, phm, phr); + else + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_BAD_ADAPTER_NUMBER); +} + /*****************************************************************************/ /* SUBSYSTEM */ @@ -491,13 +494,11 @@ static void subsys_create_adapter(struct hpi_message *phm, } /** delete an adapter - required by WDM driver */ -static void subsys_delete_adapter(struct hpi_message *phm, - struct hpi_response *phr) +static void adapter_delete(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) { - struct hpi_adapter_obj *pao; struct hpi_hw_obj *phw; - pao = hpi_find_adapter(phm->obj_index); if (!pao) { phr->error = HPI_ERROR_INVALID_OBJ_INDEX; return; @@ -563,11 +564,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, } err = adapter_boot_load_dsp(pao, pos_error_code); - if (err) + if (err) { + HPI_DEBUG_LOG(ERROR, "DSP code load failed\n"); /* no need to clean up as SubSysCreateAdapter */ /* calls DeleteAdapter on error. */ return err; - + } HPI_DEBUG_LOG(INFO, "load DSP code OK\n"); /* allow boot load even if mem alloc wont work */ @@ -604,6 +606,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, control_cache.number_of_controls, interface->control_cache.size_in_bytes, p_control_cache_virtual); + if (!phw->p_cache) err = HPI_ERROR_MEMORY_ALLOC; } @@ -675,16 +678,14 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, } /** Free memory areas allocated by adapter - * this routine is called from SubSysDeleteAdapter, + * this routine is called from AdapterDelete, * and SubSysCreateAdapter if duplicate index */ static void delete_adapter_obj(struct hpi_adapter_obj *pao) { - struct hpi_hw_obj *phw; + struct hpi_hw_obj *phw = pao->priv; int i; - phw = pao->priv; - if (hpios_locked_mem_valid(&phw->h_control_cache)) { hpios_locked_mem_free(&phw->h_control_cache); hpi_free_control_cache(phw->p_cache); @@ -1275,6 +1276,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, case HPI_ADAPTER_FAMILY_ASI(0x6300): boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400); break; + case HPI_ADAPTER_FAMILY_ASI(0x5500): case HPI_ADAPTER_FAMILY_ASI(0x5600): case HPI_ADAPTER_FAMILY_ASI(0x6500): boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600); @@ -2059,7 +2061,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us) static void send_dsp_command(struct hpi_hw_obj *phw, int cmd) { struct bus_master_interface *interface = phw->p_interface_buffer; - u32 r; interface->host_cmd = cmd; @@ -2088,7 +2089,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, phr->specific_error = sizeof(interface->u); phr->size = sizeof(struct hpi_response_header); HPI_DEBUG_LOG(ERROR, - "message len %d too big for buffer %zd \n", phm->size, + "message len %d too big for buffer %ld \n", phm->size, sizeof(interface->u)); return 0; } diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index af678be0aa1..d829a65f47d 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -294,7 +294,7 @@ enum HPI_CONTROL_ATTRIBUTES { /* These defines are used to fill in protocol information for an Ethernet packet sent using HMI on CS18102 */ -/** ID supplied by Cirrius for ASI packets. */ +/** ID supplied by Cirrus for ASI packets. */ #define HPI_ETHERNET_PACKET_ID 0x85 /** Simple packet - no special routing required */ #define HPI_ETHERNET_PACKET_V1 0x01 @@ -307,7 +307,7 @@ enum HPI_CONTROL_ATTRIBUTES { /** This packet must make its way to the host across the HPI interface */ #define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41 -#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */ +#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */ /** Default network timeout in milli-seconds. */ #define HPI_ETHERNET_TIMEOUT_MS 500 @@ -397,14 +397,14 @@ enum HPI_FUNCTION_IDS { HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1), HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2), HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3), - HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), + /* HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), */ HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5), HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6), - HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), + /* HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), */ HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8), HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9), - HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), - HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), + /* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */ + /* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */ HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12), HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13), HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14), @@ -433,7 +433,8 @@ enum HPI_FUNCTION_IDS { HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18), HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19), HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20), -#define HPI_ADAPTER_FUNCTION_COUNT 20 + HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21), +#define HPI_ADAPTER_FUNCTION_COUNT 21 HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1), HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2), @@ -1561,8 +1562,6 @@ void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr); u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource, u16 *pw_adapter_index); -u16 hpi_subsys_delete_adapter(u16 adapter_index); - u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status); diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index 3e9c5c28976..0428f285725 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -667,7 +667,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) phr->u.s.num_adapters = adapters.gw_num_adapters; break; case HPI_SUBSYS_CREATE_ADAPTER: - case HPI_SUBSYS_DELETE_ADAPTER: break; default: phr->error = HPI_ERROR_INVALID_FUNC; diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index bcbdf30a6aa..888e37966c3 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -211,24 +211,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, HPIMSGX__init(phm, phr); break; - case HPI_SUBSYS_DELETE_ADAPTER: - HPIMSGX__cleanup(phm->obj_index, h_owner); - { - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_CLOSE); - hm.adapter_index = phm->obj_index; - hw_entry_point(&hm, &hr); - } - if ((phm->obj_index < HPI_MAX_ADAPTERS) - && hpi_entry_points[phm->obj_index]) { - hpi_entry_points[phm->obj_index] (phm, phr); - hpi_entry_points[phm->obj_index] = NULL; - } else - phr->error = HPI_ERROR_INVALID_OBJ_INDEX; - - break; default: /* Must explicitly handle every subsys message in this switch */ hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, @@ -247,6 +229,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr, case HPI_ADAPTER_CLOSE: adapter_close(phm, phr); break; + case HPI_ADAPTER_DELETE: + HPIMSGX__cleanup(phm->adapter_index, h_owner); + { + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_CLOSE); + hm.adapter_index = phm->adapter_index; + hw_entry_point(&hm, &hr); + } + hw_entry_point(phm, phr); + break; + default: hw_entry_point(phm, phr); break; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index cd624f13ff8..484f4118986 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -25,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions #include "hpidebug.h" #include "hpimsgx.h" #include "hpioctl.h" +#include "hpicmn.h" #include #include @@ -161,26 +162,24 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } - pa = &adapters[hm->h.adapter_index]; + switch (hm->h.function) { + case HPI_SUBSYS_CREATE_ADAPTER: + case HPI_ADAPTER_DELETE: + /* Application must not use these functions! */ + hr->h.size = sizeof(hr->h); + hr->h.error = HPI_ERROR_INVALID_OPERATION; + hr->h.function = hm->h.function; + uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); + if (uncopied_bytes) + err = -EFAULT; + else + err = 0; + goto out; + } + hr->h.size = res_max_size; if (hm->h.object == HPI_OBJ_SUBSYSTEM) { - switch (hm->h.function) { - case HPI_SUBSYS_CREATE_ADAPTER: - case HPI_SUBSYS_DELETE_ADAPTER: - /* Application must not use these functions! */ - hr->h.size = sizeof(hr->h); - hr->h.error = HPI_ERROR_INVALID_OPERATION; - hr->h.function = hm->h.function; - uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); - if (uncopied_bytes) - err = -EFAULT; - else - err = 0; - goto out; - - default: - hpi_send_recv_f(&hm->m0, &hr->r0, file); - } + hpi_send_recv_f(&hm->m0, &hr->r0, file); } else { u16 __user *ptr = NULL; u32 size = 0; @@ -188,8 +187,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* -1=no data 0=read from user mem, 1=write to user mem */ int wrflag = -1; u32 adapter = hm->h.adapter_index; + pa = &adapters[adapter]; - if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) { + if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) { hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER, HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER_NUMBER); @@ -395,17 +395,20 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, adapter.index = hr.u.s.adapter_index; adapter.type = hr.u.s.adapter_type; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_OPEN); hm.adapter_index = adapter.index; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - err = hpi_adapter_open(adapter.index); - if (err) + if (hr.error) goto err; adapter.snd_card_asihpi = NULL; /* WARNING can't init mutex in 'adapter' * and then copy it to adapters[] ?!?! */ - adapters[hr.u.s.adapter_index] = adapter; + adapters[adapter.index] = adapter; mutex_init(&adapters[adapter.index].mutex); pci_set_drvdata(pci_dev, &adapters[adapter.index]); @@ -440,10 +443,9 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev) struct hpi_adapter *pa; pa = pci_get_drvdata(pci_dev); - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_DELETE_ADAPTER); - hm.obj_index = pa->index; - hm.adapter_index = HPI_ADAPTER_INDEX_INVALID; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_DELETE); + hm.adapter_index = pa->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); /* unmap PCI memory space, mapped during device init. */ -- cgit v1.2.3 From 42258daba22897f9859b0f3cb42700322b7c16bc Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Tue, 5 Apr 2011 20:55:48 +1200 Subject: ALSA: asihpi: Minor cleanups Remove some unneeded defintions Use %pR to print resources Make some data const Consistent braces for else Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpi_internal.h | 2 -- sound/pci/asihpi/hpicmn.c | 9 +++++++-- sound/pci/asihpi/hpicmn.h | 2 ++ sound/pci/asihpi/hpioctl.c | 9 +++------ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index d829a65f47d..4fab1595a6a 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -1583,9 +1583,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR); /*////////////////////////////////////////////////////////////////////////// */ /* declarations for individual HPI entry points */ -hpi_handler_func HPI_1000; hpi_handler_func HPI_6000; hpi_handler_func HPI_6205; -hpi_handler_func HPI_COMMON; #endif /* _HPI_INTERNAL_H_ */ diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index 0428f285725..b15a02e91f8 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -227,8 +227,9 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) if (info->control_type) { pC->p_info[info->control_index] = info; cached++; - } else /* dummy cache entry */ + } else { /* dummy cache entry */ pC->p_info[info->control_index] = NULL; + } byte_count += info->size_in32bit_words * 4; @@ -298,7 +299,7 @@ struct pad_ofs_size { unsigned int field_size; }; -static struct pad_ofs_size pad_desc[] = { +static const struct pad_ofs_size pad_desc[] = { HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */ HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */ HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */ @@ -617,6 +618,10 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, } } +/** Allocate control cache. + +\return Cache pointer, or NULL if allocation fails. +*/ struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count, const u32 size_in_bytes, u8 *p_dsp_control_buffer) { diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h index 590f0b69e65..d53cdf6e535 100644 --- a/sound/pci/asihpi/hpicmn.h +++ b/sound/pci/asihpi/hpicmn.h @@ -60,3 +60,5 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr); + +hpi_handler_func HPI_COMMON; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 484f4118986..d8e7047512f 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -317,7 +317,7 @@ out: int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { - int err, idx, nm; + int idx, nm; unsigned int memlen; struct hpi_message hm; struct hpi_response hr; @@ -351,11 +351,8 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, nm = HPI_MAX_ADAPTER_MEM_SPACES; for (idx = 0; idx < nm; idx++) { - HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n", - idx, pci_dev->resource[idx].name, - (unsigned long long)pci_resource_start(pci_dev, idx), - (unsigned long long)pci_resource_end(pci_dev, idx), - (unsigned long long)pci_resource_flags(pci_dev, idx)); + HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx, + &pci_dev->resource[idx]); if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { memlen = pci_resource_len(pci_dev, idx); -- cgit v1.2.3 From e217b960e4f82610946fcad764b8af017a4811c0 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Wed, 30 Mar 2011 20:00:39 +0800 Subject: ALSA: emu10k1 - Remove CLFE-related controls for SB Live! Platinum CT4760P SB Live! Platinum CT4760P is just a 4 channels sound card with STAC9721 and Philips UDA1334 DAC. Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emumixer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 05afe06e353..1b50a23232b 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1913,6 +1913,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, for (; *c; c += 2) rename_ctl(card, c[0], c[1]); + if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */ + remove_ctl(card, "Center Playback Volume"); + remove_ctl(card, "LFE Playback Volume"); + remove_ctl(card, "Wave Center Playback Volume"); + remove_ctl(card, "Wave LFE Playback Volume"); + } if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */ rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume"); rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume"); -- cgit v1.2.3 From 9cdc352936311eea55624cbabafda296b99ff137 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 11 Apr 2011 17:56:32 +0200 Subject: ALSA: usb-audio: Add quirks for Audio Kontrol 6 This new device by Native Instruments is also compliant to the USB standard v2.0, but hides this detail at when connected. It needs the same boot quirks than other models, and also has two non-class-compliant mixer controls. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 17 +++++++++++++++++ sound/usb/quirks-table.h | 6 ++++++ sound/usb/quirks.c | 1 + 3 files changed, 24 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 73dcc8256bc..4a7ad7ed62f 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -398,6 +398,17 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, return 0; } +static struct snd_kcontrol_new snd_nativeinstruments_ak6_mixers[] = { + { + .name = "Direct Monitor Channel 1+2", + .private_value = _MAKE_NI_CONTROL(0x03, 0x03), + }, + { + .name = "Direct Monitor Channel 3+4", + .private_value = _MAKE_NI_CONTROL(0x03, 0x05), + }, +}; + static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { { .name = "Direct Thru Channel A", @@ -526,6 +537,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_xonar_u1_controls_create(mixer); break; + case USB_ID(0x17cc, 0x1001): /* Audio Kontrol 6 */ + err = snd_nativeinstruments_create_mixer(mixer, + snd_nativeinstruments_ak6_mixers, + ARRAY_SIZE(snd_nativeinstruments_ak6_mixers)); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c66d3f64dcf..54e18c181a1 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2331,6 +2331,12 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* Native Instruments MK2 series */ +{ + /* Audio Kontrol 6 */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x17cc, + .idProduct = 0x1000, +}, { /* Traktor Audio 6 */ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 355759bad58..2452edd2f14 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -539,6 +539,7 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, /* Access Music VirusTI Desktop */ return snd_usb_accessmusic_boot_quirk(dev); + case USB_ID(0x17cc, 0x1000): /* Audio Kontrol 6 */ case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */ case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */ return snd_usb_nativeinstruments_boot_quirk(dev); -- cgit v1.2.3 From b6a48404088d91514b9e78c3237976a5aa87cb14 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Fri, 15 Apr 2011 11:42:42 +0800 Subject: ALSA: emu10k1 - Remove "Front" controls only for STAC9758/59 Remove "Front Playback Volume" and "Front Playback Switch" from emu10k1 only for STAC9758/59 Since commit 7eae36fbd5ea9db3d3fe0d671199121be782a5b3 "Fix the confliction of 'Front' control", the "Front Playback Volume" control created by commit edf8e4565c44bffbb4d09e8984df941d0ae9e6e8 "emu10k1: Front channels via fxbus 8 and 9" was removed "Front Playback Volume" and "Surround Playback Volume" have same dB range since I2S DAC of SB Live! and SB Live! Platinum does not has any hardware volume control. Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emumixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 1b50a23232b..9d890a5aec5 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1729,8 +1729,6 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Master Mono Playback Volume", "PCM Out Path & Mute", "Mono Output Select", - "Front Playback Switch", - "Front Playback Volume", "Surround Playback Switch", "Surround Playback Volume", "Center Playback Switch", @@ -1879,6 +1877,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, emu->rear_ac97 = 1; snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT); snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202); + remove_ctl(card,"Front Playback Volume"); + remove_ctl(card,"Front Playback Switch"); } /* remove unused AC97 controls */ snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202); -- cgit v1.2.3 From e66d74ced12699ed9b7ac62f68b44d842a817ecd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Mar 2011 18:20:46 +0100 Subject: ALSA: asihpi - Use %zd for size_t argument in error message (again) This was reverted mistakenly in the recent update patch. Fixed again. Reported-by: Randy Dunlap Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpi6205.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 53037342cd2..3b5562acb55 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -2089,7 +2089,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, phr->specific_error = sizeof(interface->u); phr->size = sizeof(struct hpi_response_header); HPI_DEBUG_LOG(ERROR, - "message len %d too big for buffer %ld \n", phm->size, + "message len %d too big for buffer %zd \n", phm->size, sizeof(interface->u)); return 0; } -- cgit v1.2.3 From 30282f96d1eef33be774d4ecf4bddba30a6152ec Mon Sep 17 00:00:00 2001 From: Risto Suominen Date: Thu, 21 Apr 2011 21:54:09 +0300 Subject: ALSA: powermac - Correct lineout detection on PowerMac G4 DA Correct lineout (Pro Speaker) detection on PowerMac G4 Digital Audio (Tumbler). Signed-off-by: Risto Suominen Signed-off-by: Takashi Iwai --- sound/ppc/tumbler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 961d9829769..9cea84c3e0c 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1000,7 +1000,7 @@ static void device_change_handler(struct work_struct *work) chip->lineout_sw_ctl); if (mix->anded_reset) msleep(10); - check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify, + check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify, chip->speaker_sw_ctl); } else { /* unmute speaker, mute others */ -- cgit v1.2.3 From 8ae9572b5b08f1d2a2ea6613f59d00f741b38b2d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 23 Apr 2011 20:56:43 +0200 Subject: ALSA: 6fire: use the kernel's built-in bit reverse table Signed-off-by: Daniel Mack Cc: Torsten Schenk Signed-off-by: Takashi Iwai --- sound/usb/6fire/firmware.c | 29 ++--------------------------- sound/usb/Kconfig | 1 + 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index e720035fbd1..45ffa12f0da 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -15,6 +15,7 @@ */ #include +#include #include "firmware.h" #include "chip.h" @@ -27,32 +28,6 @@ enum { FPGA_BUFSIZE = 512, FPGA_EP = 2 }; -static const u8 BIT_REVERSE_TABLE[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, - 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, - 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, - 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, - 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, - 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, - 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, - 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, - 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, - 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, - 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, - 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, - 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, - 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, - 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, - 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, - 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, - 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, - 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, - 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, - 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, - 0xbf, 0x7f, 0xff }; - /* * wMaxPacketSize of pcm endpoints. * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c @@ -338,7 +313,7 @@ static int usb6fire_fw_fpga_upload( while (c != end) { for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) - buffer[i] = BIT_REVERSE_TABLE[(u8) *c]; + buffer[i] = byte_rev_table[(u8) *c]; ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index cc47a9caec9..8beb77563da 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -101,6 +101,7 @@ config SND_USB_US122L config SND_USB_6FIRE tristate "TerraTec DMX 6Fire USB" select FW_LOADER + select BITREVERSE select SND_RAWMIDI select SND_PCM help -- cgit v1.2.3 From 13eb4ab8ca719c852ae5fbd6e803afa333ad569a Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Tue, 26 Apr 2011 12:15:23 +0800 Subject: ALSA: au88x0 - Use a better name for pcm devices of au88x0 Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/au88x0/au8810.h | 2 +- sound/pci/au88x0/au8820.h | 2 +- sound/pci/au88x0/au8830.h | 2 +- sound/pci/au88x0/au88x0_pcm.c | 13 +++++++------ 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h index 5d69c31fe3f..79fbee3845e 100644 --- a/sound/pci/au88x0/au8810.h +++ b/sound/pci/au88x0/au8810.h @@ -4,7 +4,7 @@ #define CHIP_AU8810 -#define CARD_NAME "Aureal Advantage 3D Sound Processor" +#define CARD_NAME "Aureal Advantage" #define CARD_NAME_SHORT "au8810" #define NR_ADB 0x10 diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h index abbe85e4f7a..cafdb9668a3 100644 --- a/sound/pci/au88x0/au8820.h +++ b/sound/pci/au88x0/au8820.h @@ -11,7 +11,7 @@ #define CHIP_AU8820 -#define CARD_NAME "Aureal Vortex 3D Sound Processor" +#define CARD_NAME "Aureal Vortex" #define CARD_NAME_SHORT "au8820" /* Number of ADB and WT channels */ diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h index 04ece1b1c21..999b29ab34a 100644 --- a/sound/pci/au88x0/au8830.h +++ b/sound/pci/au88x0/au8830.h @@ -11,7 +11,7 @@ #define CHIP_AU8830 -#define CARD_NAME "Aureal Vortex 2 3D Sound Processor" +#define CARD_NAME "Aureal Vortex 2" #define CARD_NAME_SHORT "au8830" #define NR_ADB 0x20 diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 5439d662d10..04b10fdb0d7 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -423,11 +423,11 @@ static struct snd_pcm_ops snd_vortex_playback_ops = { */ static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = { - "AU88x0 ADB", - "AU88x0 SPDIF", - "AU88x0 A3D", - "AU88x0 WT", - "AU88x0 I2S", + CARD_NAME " ADB", + CARD_NAME " SPDIF", + CARD_NAME " A3D", + CARD_NAME " WT", + CARD_NAME " I2S", }; static char *vortex_pcm_name[VORTEX_PCM_LAST] = { "adb", @@ -524,7 +524,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) nr_capt, &pcm); if (err < 0) return err; - strcpy(pcm->name, vortex_pcm_name[idx]); + snprintf(pcm->name, sizeof(pcm->name), + "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]); chip->pcm[idx] = pcm; // This is an evil hack, but it saves a lot of duplicated code. VORTEX_PCM_TYPE(pcm) = idx; -- cgit v1.2.3 From 59bb7f0eebe69aa32a5c7917a23a7da1c5667d73 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 Apr 2011 09:58:43 +0200 Subject: ALSA: usb-audio - Don't expose broken dB ranges Some crappy USB-audio devices give broken dB ranges, e.g. both min and max are 0dB. This confuses the volume control that prefers dB expression such as alsactl or PulseAudio. In such a case, it's much better not to expose the broken dB information. Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 5e477571660..c8c28cd7553 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1097,11 +1097,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); if (control == UAC_FU_VOLUME) { - kctl->tlv.c = mixer_vol_tlv; - kctl->vd[0].access |= - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; check_mapped_dB(map, cval); + if (cval->dBmin < cval->dBmax) { + kctl->tlv.c = mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } } break; -- cgit v1.2.3 From 14219d06592025541559027d0fd8f96ef75f313c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 9 May 2011 23:39:26 +0200 Subject: ALSA: tea575x: unify read/write functions Implement generic read/write functions to access TEA575x tuners. They're now implemented 4 times (once in es1968 and 3 times in fm801). This also allows mute to work on all cards. Also improve tuner detection/initialization. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 13 ++++-- sound/i2c/other/tea575x-tuner.c | 98 ++++++++++++++++++++++++++++++----------- 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 5aade569439..e50cb2934ef 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -26,12 +26,17 @@ #include #include +#define TEA575X_DATA (1 << 0) +#define TEA575X_CLK (1 << 1) +#define TEA575X_WREN (1 << 2) +#define TEA575X_MOST (1 << 3) + struct snd_tea575x; struct snd_tea575x_ops { - void (*write)(struct snd_tea575x *tea, unsigned int val); - unsigned int (*read)(struct snd_tea575x *tea); - void (*mute)(struct snd_tea575x *tea, unsigned int mute); + void (*set_pins)(struct snd_tea575x *tea, u8 pins); + u8 (*get_pins)(struct snd_tea575x *tea); + void (*set_direction)(struct snd_tea575x *tea, bool output); }; struct snd_tea575x { @@ -49,7 +54,7 @@ struct snd_tea575x { void *private_data; }; -void snd_tea575x_init(struct snd_tea575x *tea); +int snd_tea575x_init(struct snd_tea575x *tea); void snd_tea575x_exit(struct snd_tea575x *tea); #endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 9f35f378a17..31f9795daca 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -77,11 +77,65 @@ static struct v4l2_queryctrl radio_qctrl[] = { * lowlevel part */ +static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) +{ + u16 l; + u8 data; + + tea->ops->set_direction(tea, 1); + udelay(16); + + for (l = 25; l > 0; l--) { + data = (val >> 24) & TEA575X_DATA; + val <<= 1; /* shift data */ + tea->ops->set_pins(tea, data | TEA575X_WREN); + udelay(2); + tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK); + udelay(2); + tea->ops->set_pins(tea, data | TEA575X_WREN); + udelay(2); + } + + if (!tea->mute) + tea->ops->set_pins(tea, 0); +} + +static unsigned int snd_tea575x_read(struct snd_tea575x *tea) +{ + u16 l, rdata; + u32 data = 0; + + tea->ops->set_direction(tea, 0); + tea->ops->set_pins(tea, 0); + udelay(16); + + for (l = 24; l--;) { + tea->ops->set_pins(tea, TEA575X_CLK); + udelay(2); + if (!l) + tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1; + tea->ops->set_pins(tea, 0); + udelay(2); + data <<= 1; /* shift data */ + rdata = tea->ops->get_pins(tea); + if (!l) + tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1; + if (rdata & TEA575X_DATA) + data++; + udelay(2); + } + + if (tea->mute) + tea->ops->set_pins(tea, TEA575X_WREN); + + return data; +} + static void snd_tea575x_get_freq(struct snd_tea575x *tea) { unsigned long freq; - freq = tea->ops->read(tea) & TEA575X_BIT_FREQ_MASK; + freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK; /* freq *= 12.5 */ freq *= 125; freq /= 10; @@ -111,7 +165,7 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) tea->val &= ~TEA575X_BIT_FREQ_MASK; tea->val |= freq & TEA575X_BIT_FREQ_MASK; - tea->ops->write(tea, tea->val); + snd_tea575x_write(tea, tea->val); } /* @@ -139,7 +193,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, if (v->index > 0) return -EINVAL; - tea->ops->read(tea); + snd_tea575x_read(tea); strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; @@ -233,10 +287,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (tea->ops->mute) { - ctrl->value = tea->mute; - return 0; - } + ctrl->value = tea->mute; + return 0; } return -EINVAL; } @@ -248,11 +300,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (tea->ops->mute) { - tea->ops->mute(tea, ctrl->value); + if (tea->mute != ctrl->value) { tea->mute = ctrl->value; - return 0; + snd_tea575x_set_freq(tea); } + return 0; } return -EINVAL; } @@ -317,18 +369,16 @@ static struct video_device tea575x_radio = { /* * initialize all the tea575x chips */ -void snd_tea575x_init(struct snd_tea575x *tea) +int snd_tea575x_init(struct snd_tea575x *tea) { int retval; - unsigned int val; struct video_device *tea575x_radio_inst; - val = tea->ops->read(tea); - if (val == 0x1ffffff || val == 0) { - snd_printk(KERN_ERR - "tea575x-tuner: Cannot find TEA575x chip\n"); - return; - } + tea->mute = 1; + + snd_tea575x_write(tea, 0x55AA); + if (snd_tea575x_read(tea) != 0x55AA) + return -ENODEV; tea->in_use = 0; tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; @@ -337,7 +387,7 @@ void snd_tea575x_init(struct snd_tea575x *tea) tea575x_radio_inst = video_device_alloc(); if (tea575x_radio_inst == NULL) { printk(KERN_ERR "tea575x-tuner: not enough memory\n"); - return; + return -ENOMEM; } memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio)); @@ -352,17 +402,13 @@ void snd_tea575x_init(struct snd_tea575x *tea) if (retval) { printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); kfree(tea575x_radio_inst); - return; + return retval; } snd_tea575x_set_freq(tea); - - /* mute on init */ - if (tea->ops->mute) { - tea->ops->mute(tea, 1); - tea->mute = 1; - } tea->vd = tea575x_radio_inst; + + return 0; } void snd_tea575x_exit(struct snd_tea575x *tea) -- cgit v1.2.3 From 72587173cc31bb96db63e347b94ad0ef84878cea Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 9 May 2011 23:39:37 +0200 Subject: ALSA: es1968: convert TEA575x support to new interface Use common functions to access TEA575x tuner - remove original read/write functions and provide new pin manipulation functions instead. Tested with SF64-PCE2 card. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/es1968.c | 95 ++++++++++++++---------------------------------------- 1 file changed, 24 insertions(+), 71 deletions(-) diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index faf9138970a..75c5e0ed695 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2591,96 +2591,48 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip) #define STR_WREN 0x0100 /* GPIO8 */ #define STR_MOST 0x0200 /* GPIO9 */ -static void snd_es1968_tea575x_write(struct snd_tea575x *tea, unsigned int val) +static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) { struct es1968 *chip = tea->private_data; unsigned long io = chip->io_port + GPIO_DATA; - u16 l, bits; - u16 omask, odir; + u16 val = 0; - omask = inw(io + IO_MASK); - odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); - outw(odir | STR_DATA, io + IO_DIR); - outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); - udelay(16); + val |= (pins & TEA575X_DATA) ? STR_DATA : 0; + val |= (pins & TEA575X_CLK) ? STR_CLK : 0; + val |= (pins & TEA575X_WREN) ? STR_WREN : 0; - for (l = 25; l; l--) { - bits = ((val >> 18) & STR_DATA) | STR_WREN; - val <<= 1; /* shift data */ - outw(bits, io); /* start strobe */ - udelay(2); - outw(bits | STR_CLK, io); /* HI level */ - udelay(2); - outw(bits, io); /* LO level */ - udelay(4); - } - - if (!tea->mute) - outw(0, io); - - udelay(4); - outw(omask, io + IO_MASK); - outw(odir, io + IO_DIR); - msleep(125); + outw(val, io); } -static unsigned int snd_es1968_tea575x_read(struct snd_tea575x *tea) +static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea) { struct es1968 *chip = tea->private_data; unsigned long io = chip->io_port + GPIO_DATA; - u16 l, rdata; - u32 data = 0; - u16 omask; - - omask = inw(io + IO_MASK); - outw(~(STR_CLK | STR_WREN), io + IO_MASK); - outw(0, io); - udelay(16); - - for (l = 24; l--;) { - outw(STR_CLK, io); /* HI state */ - udelay(2); - if (!l) - tea->tuned = inw(io) & STR_MOST ? 0 : 1; - outw(0, io); /* LO state */ - udelay(2); - data <<= 1; /* shift data */ - rdata = inw(io); - if (!l) - tea->stereo = (rdata & STR_MOST) ? 0 : 1; - else if (l && rdata & STR_DATA) - data++; - udelay(2); - } - - if (tea->mute) - outw(STR_WREN, io); - - udelay(4); - outw(omask, io + IO_MASK); + u16 val = inw(io); - return data & 0x3ffe; + return (val & STR_DATA) ? TEA575X_DATA : 0 | + (val & STR_MOST) ? TEA575X_MOST : 0; } -static void snd_es1968_tea575x_mute(struct snd_tea575x *tea, unsigned int mute) +static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output) { struct es1968 *chip = tea->private_data; unsigned long io = chip->io_port + GPIO_DATA; - u16 omask; + u16 odir = inw(io + IO_DIR); - omask = inw(io + IO_MASK); - outw(~STR_WREN, io + IO_MASK); - tea->mute = mute; - outw(tea->mute ? STR_WREN : 0, io); - udelay(4); - outw(omask, io + IO_MASK); - msleep(125); + if (output) { + outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); + outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR); + } else { + outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK); + outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR); + } } static struct snd_tea575x_ops snd_es1968_tea_ops = { - .write = snd_es1968_tea575x_write, - .read = snd_es1968_tea575x_read, - .mute = snd_es1968_tea575x_mute, + .set_pins = snd_es1968_tea575x_set_pins, + .get_pins = snd_es1968_tea575x_get_pins, + .set_direction = snd_es1968_tea575x_set_direction, }; #endif @@ -2845,7 +2797,8 @@ static int __devinit snd_es1968_create(struct snd_card *card, chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; chip->tea.ops = &snd_es1968_tea_ops; - snd_tea575x_init(&chip->tea); + if (!snd_tea575x_init(&chip->tea)) + printk(KERN_INFO "es1968: detected TEA575x radio\n"); #endif *chip_ret = chip; -- cgit v1.2.3 From 938a1566b162aa485acd8dea2ebd92d92dfa0a45 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 9 May 2011 23:39:51 +0200 Subject: ALSA: fm801: convert TEA575x support to new interface Use common functions to access TEA575x tuner - remove original read/write functions and provide new pin manipulation functions instead. Also convert the original triple implementation to a simple GPIO pin map. Tested with SF256-PCP and SF64-PCR (added the GPIO pin for MO/ST signal for them). SF256-PCS untested (pin for MO/ST signal is a guess). Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/fm801.c | 336 +++++++++--------------------------------------------- 1 file changed, 57 insertions(+), 279 deletions(-) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index f4dc1c77202..1db5ead62a9 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -717,308 +717,86 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc #ifdef TEA575X_RADIO -/* 256PCS GPIO numbers */ -#define TEA_256PCS_DATA 1 -#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */ -#define TEA_256PCS_BUS_CLOCK 3 - -static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val) -{ - struct fm801 *chip = tea->private_data; - unsigned short reg; - int i = 25; - - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); - /* use GPIO lines and set write enable bit */ - reg |= FM801_GPIO_GS(TEA_256PCS_DATA) | - FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK); - /* all of lines are in the write direction */ - /* clear data and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) | - FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCS_DATA) | - FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE)); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - - while (i--) { - if (val & (1 << i)) - reg |= FM801_GPIO_GP(TEA_256PCS_DATA); - else - reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - } +/* GPIO to TEA575x maps */ +struct snd_fm801_tea575x_gpio { + u8 data, clk, wren, most; +}; - /* and reset the write enable bit */ - reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) | - FM801_GPIO_GP(TEA_256PCS_DATA); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - spin_unlock_irq(&chip->reg_lock); -} +static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = { + { .data = 1, .clk = 3, .wren = 2, .most = 0 }, /* SF256-PCS */ + { .data = 1, .clk = 0, .wren = 2, .most = 3 }, /* SF256-PCP */ + { .data = 2, .clk = 0, .wren = 1, .most = 3 }, /* SF64-PCR */ +}; -static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea) +static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) { struct fm801 *chip = tea->private_data; - unsigned short reg; - unsigned int val = 0; - int i; - - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); - /* use GPIO lines, set data direction to input */ - reg |= FM801_GPIO_GS(TEA_256PCS_DATA) | - FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) | - FM801_GPIO_GD(TEA_256PCS_DATA) | - FM801_GPIO_GP(TEA_256PCS_DATA) | - FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE); - /* all of lines are in the write direction, except data */ - /* clear data, write enable and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK)); - - for (i = 0; i < 24; i++) { - reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - val <<= 1; - if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA)) - val |= 1; - } - - spin_unlock_irq(&chip->reg_lock); - - return val; -} - -/* 256PCPR GPIO numbers */ -#define TEA_256PCPR_BUS_CLOCK 0 -#define TEA_256PCPR_DATA 1 -#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */ + unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); + struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1]; -static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val) -{ - struct fm801 *chip = tea->private_data; - unsigned short reg; - int i = 25; + reg &= ~(FM801_GPIO_GP(gpio.data) | + FM801_GPIO_GP(gpio.clk) | + FM801_GPIO_GP(gpio.wren)); - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); - /* use GPIO lines and set write enable bit */ - reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) | - FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK); - /* all of lines are in the write direction */ - /* clear data and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) | - FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCPR_DATA) | - FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE)); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - - while (i--) { - if (val & (1 << i)) - reg |= FM801_GPIO_GP(TEA_256PCPR_DATA); - else - reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - } + reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0; + reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0; + /* WRITE_ENABLE is inverted */ + reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren); - /* and reset the write enable bit */ - reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) | - FM801_GPIO_GP(TEA_256PCPR_DATA); outw(reg, FM801_REG(chip, GPIO_CTRL)); - spin_unlock_irq(&chip->reg_lock); } -static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea) +static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea) { struct fm801 *chip = tea->private_data; - unsigned short reg; - unsigned int val = 0; - int i; - - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); - /* use GPIO lines, set data direction to input */ - reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) | - FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) | - FM801_GPIO_GD(TEA_256PCPR_DATA) | - FM801_GPIO_GP(TEA_256PCPR_DATA) | - FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE); - /* all of lines are in the write direction, except data */ - /* clear data, write enable and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK)); - - for (i = 0; i < 24; i++) { - reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - val <<= 1; - if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA)) - val |= 1; - } - - spin_unlock_irq(&chip->reg_lock); + unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); + struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1]; - return val; + return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 | + (reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0; } -/* 64PCR GPIO numbers */ -#define TEA_64PCR_BUS_CLOCK 0 -#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */ -#define TEA_64PCR_DATA 2 - -static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val) +static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output) { struct fm801 *chip = tea->private_data; - unsigned short reg; - int i = 25; + unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL)); + struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1]; - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); /* use GPIO lines and set write enable bit */ - reg |= FM801_GPIO_GS(TEA_64PCR_DATA) | - FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK); - /* all of lines are in the write direction */ - /* clear data and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) | - FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_64PCR_DATA) | - FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE)); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - - while (i--) { - if (val & (1 << i)) - reg |= FM801_GPIO_GP(TEA_64PCR_DATA); - else - reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - } - - /* and reset the write enable bit */ - reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) | - FM801_GPIO_GP(TEA_64PCR_DATA); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - spin_unlock_irq(&chip->reg_lock); -} - -static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea) -{ - struct fm801 *chip = tea->private_data; - unsigned short reg; - unsigned int val = 0; - int i; - - spin_lock_irq(&chip->reg_lock); - reg = inw(FM801_REG(chip, GPIO_CTRL)); - /* use GPIO lines, set data direction to input */ - reg |= FM801_GPIO_GS(TEA_64PCR_DATA) | - FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) | - FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) | - FM801_GPIO_GD(TEA_64PCR_DATA) | - FM801_GPIO_GP(TEA_64PCR_DATA) | - FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); - /* all of lines are in the write direction, except data */ - /* clear data, write enable and clock lines */ - reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) | - FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) | - FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK)); - - for (i = 0; i < 24; i++) { - reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK); - outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - val <<= 1; - if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA)) - val |= 1; + reg |= FM801_GPIO_GS(gpio.data) | + FM801_GPIO_GS(gpio.wren) | + FM801_GPIO_GS(gpio.clk) | + FM801_GPIO_GS(gpio.most); + if (output) { + /* all of lines are in the write direction */ + /* clear data and clock lines */ + reg &= ~(FM801_GPIO_GD(gpio.data) | + FM801_GPIO_GD(gpio.wren) | + FM801_GPIO_GD(gpio.clk) | + FM801_GPIO_GP(gpio.data) | + FM801_GPIO_GP(gpio.clk) | + FM801_GPIO_GP(gpio.wren)); + } else { + /* use GPIO lines, set data direction to input */ + reg |= FM801_GPIO_GD(gpio.data) | + FM801_GPIO_GD(gpio.most) | + FM801_GPIO_GP(gpio.data) | + FM801_GPIO_GP(gpio.most) | + FM801_GPIO_GP(gpio.wren); + /* all of lines are in the write direction, except data */ + /* clear data, write enable and clock lines */ + reg &= ~(FM801_GPIO_GD(gpio.wren) | + FM801_GPIO_GD(gpio.clk) | + FM801_GPIO_GP(gpio.clk)); } - spin_unlock_irq(&chip->reg_lock); - - return val; -} - -static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea, - unsigned int mute) -{ - struct fm801 *chip = tea->private_data; - unsigned short reg; - - spin_lock_irq(&chip->reg_lock); - - reg = inw(FM801_REG(chip, GPIO_CTRL)); - if (mute) - /* 0xf800 (mute) */ - reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); - else - /* 0xf802 (unmute) */ - reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); outw(reg, FM801_REG(chip, GPIO_CTRL)); - udelay(1); - - spin_unlock_irq(&chip->reg_lock); } -static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { - { - /* 1 = MediaForte 256-PCS */ - .write = snd_fm801_tea575x_256pcs_write, - .read = snd_fm801_tea575x_256pcs_read, - }, - { - /* 2 = MediaForte 256-PCPR */ - .write = snd_fm801_tea575x_256pcpr_write, - .read = snd_fm801_tea575x_256pcpr_read, - }, - { - /* 3 = MediaForte 64-PCR */ - .write = snd_fm801_tea575x_64pcr_write, - .read = snd_fm801_tea575x_64pcr_read, - .mute = snd_fm801_tea575x_64pcr_mute, - } +static struct snd_tea575x_ops snd_fm801_tea_ops = { + .set_pins = snd_fm801_tea575x_set_pins, + .get_pins = snd_fm801_tea575x_get_pins, + .set_direction = snd_fm801_tea575x_set_direction, }; #endif @@ -1456,7 +1234,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, chip->tea.card = card; chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; - chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1]; + chip->tea.ops = &snd_fm801_tea_ops; snd_tea575x_init(&chip->tea); } #endif -- cgit v1.2.3 From d7ba858a7f7a95d1617756a83ff0717767f624fd Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Tue, 10 May 2011 23:24:15 +0200 Subject: ALSA: fm801: implement TEA575x tuner autodetection Autodetect TEA575x tuner connection type during init. This allows tuner to work out-of-the box. tea575x_tuner module parameter remains functional to force tuner type. Tested with SF256-PCP and SF64-PCR. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/fm801.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 1db5ead62a9..5aa3fd6f25f 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -53,7 +53,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * /* * Enable TEA575x tuner * 1 = MediaForte 256-PCS - * 2 = MediaForte 256-PCPR + * 2 = MediaForte 256-PCP * 3 = MediaForte 64-PCR * 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card * High 16-bits are video (radio) device number + 1 @@ -67,7 +67,7 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); module_param_array(tea575x_tuner, int, NULL, 0444); -MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only)."); +MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only)."); #define TUNER_ONLY (1<<4) #define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF) @@ -720,12 +720,13 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc /* GPIO to TEA575x maps */ struct snd_fm801_tea575x_gpio { u8 data, clk, wren, most; + char *name; }; static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = { - { .data = 1, .clk = 3, .wren = 2, .most = 0 }, /* SF256-PCS */ - { .data = 1, .clk = 0, .wren = 2, .most = 3 }, /* SF256-PCP */ - { .data = 2, .clk = 0, .wren = 1, .most = 3 }, /* SF64-PCR */ + { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" }, + { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" }, + { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" }, }; static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) @@ -1229,14 +1230,24 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef TEA575X_RADIO + chip->tea.card = card; + chip->tea.freq_fixup = 10700; + chip->tea.private_data = chip; + chip->tea.ops = &snd_fm801_tea_ops; if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && (tea575x_tuner & TUNER_TYPE_MASK) < 4) { - chip->tea.card = card; - chip->tea.freq_fixup = 10700; - chip->tea.private_data = chip; - chip->tea.ops = &snd_fm801_tea_ops; - snd_tea575x_init(&chip->tea); - } + if (snd_tea575x_init(&chip->tea)) + snd_printk(KERN_ERR "TEA575x radio not found\n"); + } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0) + /* autodetect tuner connection */ + for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) { + chip->tea575x_tuner = tea575x_tuner; + if (!snd_tea575x_init(&chip->tea)) { + snd_printk(KERN_INFO "detected TEA575x radio type %s\n", + snd_fm801_tea575x_gpios[tea575x_tuner - 1].name); + break; + } + } #endif *rchip = chip; -- cgit v1.2.3 From 3a691b28a0ca3cf4d9010c6158318159e0275d2c Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:44:51 +0200 Subject: ALSA: add Apple iSight microphone driver This adds an experimental driver for the front and rear microphones of the Apple iSight web camera. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/Kconfig | 12 + sound/firewire/Makefile | 2 + sound/firewire/isight.c | 744 ++++++++++++++++++++++++++++++++++++++++ sound/firewire/iso-resources.c | 5 + sound/firewire/packets-buffer.c | 2 + 5 files changed, 765 insertions(+) create mode 100644 sound/firewire/isight.c diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index e486f48660f..312f90d7ec6 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -22,4 +22,16 @@ config SND_FIREWIRE_SPEAKERS To compile this driver as a module, choose M here: the module will be called snd-firewire-speakers. +config SND_ISIGHT + tristate "Apple iSight microphone (EXPERIMENTAL)" + depends on EXPERIMENTAL + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for the front and rear microphones + of the Apple iSight web camera. + + To compile this driver as a module, choose M here: the module + will be called snd-isight. + endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index e5b1634d9ad..d71ed8935f7 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,6 +1,8 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o snd-firewire-speakers-objs := speakers.o +snd-isight-objs := isight.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o +obj-$(CONFIG_SND_ISIGHT) += snd-isight.o diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c new file mode 100644 index 00000000000..a6f19f57a1c --- /dev/null +++ b/sound/firewire/isight.c @@ -0,0 +1,744 @@ +/* + * Apple iSight audio driver + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lib.h" +#include "iso-resources.h" +#include "packets-buffer.h" + +#define OUI_APPLE 0x000a27 +#define MODEL_APPLE_ISIGHT 0x000008 +#define SW_ISIGHT_AUDIO 0x000010 + +#define REG_AUDIO_ENABLE 0x000 +#define AUDIO_ENABLE 0x80000000 +#define REG_DEF_AUDIO_GAIN 0x204 +#define REG_GAIN_RAW_START 0x210 +#define REG_GAIN_RAW_END 0x214 +#define REG_GAIN_DB_START 0x218 +#define REG_GAIN_DB_END 0x21c +#define REG_SAMPLE_RATE_INQUIRY 0x280 +#define REG_ISO_TX_CONFIG 0x300 +#define SPEED_SHIFT 16 +#define REG_SAMPLE_RATE 0x400 +#define RATE_48000 0x80000000 +#define REG_GAIN 0x500 +#define REG_MUTE 0x504 + +#define MAX_FRAMES_PER_PACKET 475 + +#define QUEUE_LENGTH 20 + +struct isight { + struct snd_card *card; + struct fw_unit *unit; + struct fw_device *device; + u64 audio_base; + struct fw_address_handler iris_handler; + struct snd_pcm_substream *pcm; + struct mutex mutex; + struct iso_packets_buffer buffer; + struct fw_iso_resources resources; + struct fw_iso_context *context; + bool pcm_running; + bool first_packet; + int packet_index; + u32 total_samples; + unsigned int buffer_pointer; + unsigned int period_counter; + s32 gain_min, gain_max; + unsigned int gain_tlv[4]; +}; + +struct audio_payload { + __be32 sample_count; + __be32 signature; + __be32 sample_total; + __be32 reserved; + __be16 samples[2 * MAX_FRAMES_PER_PACKET]; +}; + +MODULE_DESCRIPTION("iSight audio driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); + +static struct fw_iso_packet audio_packet = { + .payload_length = sizeof(struct audio_payload), + .interrupt = 1, +}; + +static void isight_update_pointers(struct isight *isight, unsigned int count) +{ + struct snd_pcm_runtime *runtime = isight->pcm->runtime; + unsigned int ptr; + + smp_wmb(); /* update buffer data before buffer pointer */ + + ptr = isight->buffer_pointer; + ptr += count; + if (ptr >= runtime->buffer_size) + ptr -= runtime->buffer_size; + ACCESS_ONCE(isight->buffer_pointer) = ptr; + + isight->period_counter += count; + if (isight->period_counter >= runtime->period_size) { + isight->period_counter -= runtime->period_size; + snd_pcm_period_elapsed(isight->pcm); + } +} + +static void isight_samples(struct isight *isight, + const __be16 *samples, unsigned int count) +{ + struct snd_pcm_runtime *runtime; + unsigned int count1; + + if (!ACCESS_ONCE(isight->pcm_running)) + return; + + runtime = isight->pcm->runtime; + if (isight->buffer_pointer + count <= runtime->buffer_size) { + memcpy(runtime->dma_area + isight->buffer_pointer * 4, + samples, count * 4); + } else { + count1 = runtime->buffer_size - isight->buffer_pointer; + memcpy(runtime->dma_area + isight->buffer_pointer * 4, + samples, count1 * 4); + samples += count1 * 2; + memcpy(runtime->dma_area, samples, (count - count1) * 4); + } + + isight_update_pointers(isight, count); +} + +static void isight_pcm_abort(struct isight *isight) +{ + unsigned long flags; + + snd_pcm_stream_lock_irqsave(isight->pcm, flags); + if (snd_pcm_running(isight->pcm)) + snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); +} + +static void isight_dropped_samples(struct isight *isight, unsigned int total) +{ + struct snd_pcm_runtime *runtime; + u32 dropped; + unsigned int count1; + + if (!ACCESS_ONCE(isight->pcm_running)) + return; + + runtime = isight->pcm->runtime; + dropped = total - isight->total_samples; + if (dropped < runtime->buffer_size) { + if (isight->buffer_pointer + dropped <= runtime->buffer_size) { + memset(runtime->dma_area + isight->buffer_pointer * 4, + 0, dropped * 4); + } else { + count1 = runtime->buffer_size - isight->buffer_pointer; + memset(runtime->dma_area + isight->buffer_pointer * 4, + 0, count1 * 4); + memset(runtime->dma_area, 0, (dropped - count1) * 4); + } + isight_update_pointers(isight, dropped); + } else { + isight_pcm_abort(isight); + } +} + +static void isight_packet(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct isight *isight = data; + const struct audio_payload *payload; + unsigned int index, length, count, total; + int err; + + if (isight->packet_index < 0) + return; + index = isight->packet_index; + payload = isight->buffer.packets[index].buffer; + length = be32_to_cpup(header) >> 16; + + if (likely(length >= 16 && + payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { + count = be32_to_cpu(payload->sample_count); + if (likely(count <= (length - 16) / 4)) { + total = be32_to_cpu(payload->sample_total); + if (unlikely(total != isight->total_samples)) { + if (!isight->first_packet) + isight_dropped_samples(isight, total); + isight->first_packet = false; + isight->total_samples = total; + } + + isight_samples(isight, payload->samples, count); + isight->total_samples += count; + } + } + + if (++index >= QUEUE_LENGTH) + index = 0; + + err = fw_iso_context_queue(isight->context, &audio_packet, + &isight->buffer.iso_buffer, + isight->buffer.packets[index].offset); + if (err < 0) { + dev_err(&isight->unit->device, "queueing error: %d\n", err); + isight_pcm_abort(isight); + isight->packet_index = -1; + return; + } + + isight->packet_index = index; +} + +static int isight_connect(struct isight *isight) +{ + int ch, err, rcode, errors = 0; + __be32 value; + +retry_after_bus_reset: + ch = fw_iso_resources_allocate(&isight->resources, + sizeof(struct audio_payload), + isight->device->max_speed); + if (ch < 0) { + err = ch; + goto error; + } + + value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); + for (;;) { + rcode = fw_run_transaction( + isight->device->card, + TCODE_WRITE_QUADLET_REQUEST, + isight->device->node_id, + isight->resources.generation, + isight->device->max_speed, + isight->audio_base + REG_ISO_TX_CONFIG, + &value, 4); + if (rcode == RCODE_COMPLETE) { + return 0; + } else if (rcode == RCODE_GENERATION) { + fw_iso_resources_free(&isight->resources); + goto retry_after_bus_reset; + } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) { + err = -EIO; + goto err_resources; + } + msleep(5); + } + +err_resources: + fw_iso_resources_free(&isight->resources); +error: + return err; +} + +static int isight_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = MAX_FRAMES_PER_PACKET * 4, + .period_bytes_max = 1024 * 1024, + .periods_min = 2, + .periods_max = UINT_MAX, + }; + struct isight *isight = substream->private_data; + + substream->runtime->hw = hardware; + + return iso_packets_buffer_init(&isight->buffer, isight->unit, + QUEUE_LENGTH, + sizeof(struct audio_payload), + DMA_FROM_DEVICE); +} + +static int isight_close(struct snd_pcm_substream *substream) +{ + struct isight *isight = substream->private_data; + + iso_packets_buffer_destroy(&isight->buffer, isight->unit); + + return 0; +} + +static int isight_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static void isight_stop_streaming(struct isight *isight) +{ + __be32 value; + + if (!isight->context) + return; + + fw_iso_context_stop(isight->context); + fw_iso_context_destroy(isight->context); + isight->context = NULL; + + value = 0; + snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_AUDIO_ENABLE, + &value, 4); + + fw_iso_resources_free(&isight->resources); +} + +static int isight_hw_free(struct snd_pcm_substream *substream) +{ + struct isight *isight = substream->private_data; + + mutex_lock(&isight->mutex); + isight_stop_streaming(isight); + mutex_unlock(&isight->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int isight_start_streaming(struct isight *isight) +{ + __be32 sample_rate; + unsigned int i; + int err; + + if (isight->context) { + if (isight->packet_index < 0) + isight_stop_streaming(isight); + else + return 0; + } + + sample_rate = cpu_to_be32(RATE_48000); + err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_SAMPLE_RATE, + &sample_rate, 4); + if (err < 0) + return err; + + err = isight_connect(isight); + if (err < 0) + goto error; + + isight->context = fw_iso_context_create(isight->device->card, + FW_ISO_CONTEXT_RECEIVE, + isight->resources.channel, + isight->device->max_speed, + 4, isight_packet, isight); + if (IS_ERR(isight->context)) { + err = PTR_ERR(isight->context); + isight->context = NULL; + goto err_resources; + } + + for (i = 0; i < QUEUE_LENGTH; ++i) { + err = fw_iso_context_queue(isight->context, &audio_packet, + &isight->buffer.iso_buffer, + isight->buffer.packets[i].offset); + if (err < 0) + goto err_context; + } + + isight->first_packet = true; + isight->packet_index = 0; + + err = fw_iso_context_start(isight->context, -1, 0, + FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/); + if (err < 0) + goto err_context; + + return 0; + +err_context: + fw_iso_context_destroy(isight->context); + isight->context = NULL; +err_resources: + fw_iso_resources_free(&isight->resources); +error: + return err; +} + +static int isight_prepare(struct snd_pcm_substream *substream) +{ + struct isight *isight = substream->private_data; + int err; + + isight->buffer_pointer = 0; + isight->period_counter = 0; + + mutex_lock(&isight->mutex); + err = isight_start_streaming(isight); + mutex_unlock(&isight->mutex); + + return err; +} + +static int isight_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct isight *isight = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ACCESS_ONCE(isight->pcm_running) = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + ACCESS_ONCE(isight->pcm_running) = false; + break; + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream) +{ + struct isight *isight = substream->private_data; + + return ACCESS_ONCE(isight->buffer_pointer); +} + +static int isight_create_pcm(struct isight *isight) +{ + static struct snd_pcm_ops ops = { + .open = isight_open, + .close = isight_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = isight_hw_params, + .hw_free = isight_hw_free, + .prepare = isight_prepare, + .trigger = isight_trigger, + .pointer = isight_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = isight; + strcpy(pcm->name, "iSight"); + isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + isight->pcm->ops = &ops; + + return 0; +} + +static int isight_gain_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + struct isight *isight = ctl->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = isight->gain_min; + info->value.integer.max = isight->gain_max; + + return 0; +} + +static int isight_gain_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct isight *isight = ctl->private_data; + __be32 gain; + int err; + + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_GAIN, &gain, 4); + if (err < 0) + return err; + + value->value.integer.value[0] = (s32)be32_to_cpu(gain); + + return 0; +} + +static int isight_gain_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct isight *isight = ctl->private_data; + __be32 gain; + + if (value->value.integer.value[0] < isight->gain_min || + value->value.integer.value[0] > isight->gain_max) + return -EINVAL; + + gain = cpu_to_be32(value->value.integer.value[0]); + return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_GAIN, &gain, 4); +} + +static int isight_mute_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct isight *isight = ctl->private_data; + __be32 mute; + int err; + + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_MUTE, &mute, 4); + if (err < 0) + return err; + + value->value.integer.value[0] = !mute; + + return 0; +} + +static int isight_mute_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct isight *isight = ctl->private_data; + __be32 mute; + + mute = (__force __be32)!value->value.integer.value[0]; + return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_MUTE, &mute, 4); +} + +static int isight_create_mixer(struct isight *isight) +{ + static const struct snd_kcontrol_new gain_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = isight_gain_info, + .get = isight_gain_get, + .put = isight_gain_put, + }; + static const struct snd_kcontrol_new mute_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Switch", + .info = snd_ctl_boolean_mono_info, + .get = isight_mute_get, + .put = isight_mute_put, + }; + __be32 value; + struct snd_kcontrol *ctl; + int err; + + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_GAIN_RAW_START, + &value, 4); + if (err < 0) + return err; + isight->gain_min = be32_to_cpu(value); + + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_GAIN_RAW_END, + &value, 4); + if (err < 0) + return err; + isight->gain_max = be32_to_cpu(value); + + isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; + isight->gain_tlv[1] = 2 * sizeof(unsigned int); + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_GAIN_DB_START, + &value, 4); + if (err < 0) + return err; + isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; + err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + REG_GAIN_DB_END, + &value, 4); + if (err < 0) + return err; + isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; + + ctl = snd_ctl_new1(&gain_control, isight); + if (ctl) + ctl->tlv.p = isight->gain_tlv; + err = snd_ctl_add(isight->card, ctl); + if (err < 0) + return err; + + err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight)); + if (err < 0) + return err; + + return 0; +} + +static void isight_card_free(struct snd_card *card) +{ + struct isight *isight = card->private_data; + + fw_iso_resources_destroy(&isight->resources); + fw_unit_put(isight->unit); + fw_device_put(isight->device); + mutex_destroy(&isight->mutex); +} + +static u64 get_unit_base(struct fw_unit *unit) +{ + struct fw_csr_iterator i; + int key, value; + + fw_csr_iterator_init(&i, unit->directory); + while (fw_csr_iterator_next(&i, &key, &value)) + if (key == CSR_OFFSET) + return CSR_REGISTER_BASE + value * 4; + return 0; +} + +static int isight_probe(struct device *unit_dev) +{ + struct fw_unit *unit = fw_unit(unit_dev); + struct fw_device *fw_dev = fw_parent_device(unit); + struct snd_card *card; + struct isight *isight; + int err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card); + if (err < 0) + return err; + snd_card_set_dev(card, unit_dev); + + isight = card->private_data; + isight->card = card; + mutex_init(&isight->mutex); + isight->unit = fw_unit_get(unit); + isight->device = fw_device_get(fw_dev); + isight->audio_base = get_unit_base(unit); + if (!isight->audio_base) { + dev_err(&unit->device, "audio unit base not found\n"); + err = -ENXIO; + goto err_unit; + } + fw_iso_resources_init(&isight->resources, unit); + + card->private_free = isight_card_free; + + strcpy(card->driver, "iSight"); + strcpy(card->shortname, "Apple iSight"); + snprintf(card->longname, sizeof(card->longname), + "Apple iSight (GUID %08x%08x) at %s, S%d", + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&unit->device), 100 << fw_dev->max_speed); + strcpy(card->mixername, "iSight"); + + err = isight_create_pcm(isight); + if (err < 0) + goto error; + + err = isight_create_mixer(isight); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(unit_dev, isight); + + return 0; + +err_unit: + fw_unit_put(isight->unit); + fw_device_put(isight->device); + mutex_destroy(&isight->mutex); +error: + snd_card_free(card); + return err; +} + +static int isight_remove(struct device *dev) +{ + struct isight *isight = dev_get_drvdata(dev); + + snd_card_disconnect(isight->card); + + mutex_lock(&isight->mutex); + isight_pcm_abort(isight); + isight_stop_streaming(isight); + mutex_unlock(&isight->mutex); + + snd_card_free_when_closed(isight->card); + + return 0; +} + +static void isight_bus_reset(struct fw_unit *unit) +{ + struct isight *isight = dev_get_drvdata(&unit->device); + + mutex_lock(&isight->mutex); + if (fw_iso_resources_update(&isight->resources) < 0) { + isight_pcm_abort(isight); + isight_stop_streaming(isight); + } + mutex_unlock(&isight->mutex); +} + +static const struct ieee1394_device_id isight_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = OUI_APPLE, + .version = SW_ISIGHT_AUDIO, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, isight_id_table); + +static struct fw_driver isight_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + .probe = isight_probe, + .remove = isight_remove, + }, + .update = isight_bus_reset, + .id_table = isight_id_table, +}; + +static int __init alsa_isight_init(void) +{ + return driver_register(&isight_driver.driver); +} + +static void __exit alsa_isight_exit(void) +{ + driver_unregister(&isight_driver.driver); +} + +module_init(alsa_isight_init); +module_exit(alsa_isight_exit); diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c index 775dbd5f344..9d4a6714f9e 100644 --- a/sound/firewire/iso-resources.c +++ b/sound/firewire/iso-resources.c @@ -36,6 +36,7 @@ int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) return 0; } +EXPORT_SYMBOL(fw_iso_resources_init); /** * fw_iso_resources_destroy - destroy a resource manager @@ -48,6 +49,7 @@ void fw_iso_resources_destroy(struct fw_iso_resources *r) mutex_destroy(&r->mutex); fw_unit_put(r->unit); } +EXPORT_SYMBOL(fw_iso_resources_destroy); static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) { @@ -152,6 +154,7 @@ retry_after_bus_reset: return channel; } +EXPORT_SYMBOL(fw_iso_resources_allocate); /** * fw_iso_resources_update - update resource allocations after a bus reset @@ -203,6 +206,7 @@ int fw_iso_resources_update(struct fw_iso_resources *r) return channel; } +EXPORT_SYMBOL(fw_iso_resources_update); /** * fw_iso_resources_free - frees allocated resources @@ -230,3 +234,4 @@ void fw_iso_resources_free(struct fw_iso_resources *r) mutex_unlock(&r->mutex); } +EXPORT_SYMBOL(fw_iso_resources_free); diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c index 1e20e60ba6a..3c61ca2e615 100644 --- a/sound/firewire/packets-buffer.c +++ b/sound/firewire/packets-buffer.c @@ -60,6 +60,7 @@ err_packets: error: return err; } +EXPORT_SYMBOL(iso_packets_buffer_init); /** * iso_packets_buffer_destroy - frees packet buffer resources @@ -72,3 +73,4 @@ void iso_packets_buffer_destroy(struct iso_packets_buffer *b, fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); kfree(b->packets); } +EXPORT_SYMBOL(iso_packets_buffer_destroy); -- cgit v1.2.3 From 03c29680d49662859d14d64f8673550fa3fb2ed1 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:47:30 +0200 Subject: ALSA: isight: fix isight_pcm_abort() crashes Fix crashes in isight_pcm_abort() that happen when the driver tries to access isight->pcm->runtime which does not exist when the device is not open. Introduce a new field pcm_active to track this state. Signed-off-by: Clemens Ladisch Reported-by: Stefan Richter Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index a6f19f57a1c..0230605c917 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -56,6 +56,7 @@ struct isight { struct iso_packets_buffer buffer; struct fw_iso_resources resources; struct fw_iso_context *context; + bool pcm_active; bool pcm_running; bool first_packet; int packet_index; @@ -131,10 +132,12 @@ static void isight_pcm_abort(struct isight *isight) { unsigned long flags; - snd_pcm_stream_lock_irqsave(isight->pcm, flags); - if (snd_pcm_running(isight->pcm)) - snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); + if (ACCESS_ONCE(isight->pcm_active)) { + snd_pcm_stream_lock_irqsave(isight->pcm, flags); + if (snd_pcm_running(isight->pcm)) + snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); + } } static void isight_dropped_samples(struct isight *isight, unsigned int total) @@ -295,8 +298,17 @@ static int isight_close(struct snd_pcm_substream *substream) static int isight_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); + struct isight *isight = substream->private_data; + int err; + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + ACCESS_ONCE(isight->pcm_active) = true; + + return 0; } static void isight_stop_streaming(struct isight *isight) @@ -322,6 +334,8 @@ static int isight_hw_free(struct snd_pcm_substream *substream) { struct isight *isight = substream->private_data; + ACCESS_ONCE(isight->pcm_active) = false; + mutex_lock(&isight->mutex); isight_stop_streaming(isight); mutex_unlock(&isight->mutex); -- cgit v1.2.3 From 898732d1f1c7181fd3e94e7d7a784edb48d09d95 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:48:24 +0200 Subject: ALSA: isight: fix packet requeueing After handling a received packet, we want to resubmit the same packet, so do not increase the packet index too early. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 0230605c917..4e334919a70 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -198,9 +198,6 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, } } - if (++index >= QUEUE_LENGTH) - index = 0; - err = fw_iso_context_queue(isight->context, &audio_packet, &isight->buffer.iso_buffer, isight->buffer.packets[index].offset); @@ -211,6 +208,8 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, return; } + if (++index >= QUEUE_LENGTH) + index = 0; isight->packet_index = index; } -- cgit v1.2.3 From f2934cd499ba2c7f605787508b4cfcfa3a45b0a4 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:49:02 +0200 Subject: ALSA: isight: fix divide error when queueing packets Set the .header_size field when queueing packets to avoid a division by zero. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 4e334919a70..10a9b9b0b2c 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -82,6 +82,7 @@ MODULE_LICENSE("GPL v2"); static struct fw_iso_packet audio_packet = { .payload_length = sizeof(struct audio_payload), .interrupt = 1, + .header_length = 4, }; static void isight_update_pointers(struct isight *isight, unsigned int count) -- cgit v1.2.3 From 8839eedafd2e91e5b124730825e9b39b1ff493dd Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 11 May 2011 10:49:58 +0200 Subject: ALSA: isight: add AudioEnable register write which is needed to get the iSight to talk. Signed-off-by: Stefan Richter Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 10a9b9b0b2c..1a8da2614db 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -345,7 +345,7 @@ static int isight_hw_free(struct snd_pcm_substream *substream) static int isight_start_streaming(struct isight *isight) { - __be32 sample_rate; + __be32 value; unsigned int i; int err; @@ -356,10 +356,10 @@ static int isight_start_streaming(struct isight *isight) return 0; } - sample_rate = cpu_to_be32(RATE_48000); + value = cpu_to_be32(RATE_48000); err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, isight->audio_base + REG_SAMPLE_RATE, - &sample_rate, 4); + &value, 4); if (err < 0) return err; @@ -367,6 +367,13 @@ static int isight_start_streaming(struct isight *isight) if (err < 0) goto error; + value = cpu_to_be32(AUDIO_ENABLE); + err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_AUDIO_ENABLE, + &value, 4); + if (err < 0) + goto err_resources; + isight->context = fw_iso_context_create(isight->device->card, FW_ISO_CONTEXT_RECEIVE, isight->resources.channel, @@ -400,6 +407,10 @@ err_context: fw_iso_context_destroy(isight->context); isight->context = NULL; err_resources: + value = 0; + snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_AUDIO_ENABLE, + &value, 4); fw_iso_resources_free(&isight->resources); error: return err; -- cgit v1.2.3 From ac34dad26e6786257ef54d8df4f883825bea02eb Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 11 May 2011 10:52:21 +0200 Subject: ALSA: isight: wrap up register accesses Signed-off-by: Stefan Richter [cl: removed superfluous variable] Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 76 +++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 1a8da2614db..4d2edcfdbbc 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -5,6 +5,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ +#include #include #include #include @@ -311,23 +312,28 @@ static int isight_hw_params(struct snd_pcm_substream *substream, return 0; } -static void isight_stop_streaming(struct isight *isight) +static int reg_read(struct isight *isight, int offset, __be32 *value) { - __be32 value; + return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, + isight->audio_base + offset, value, 4); +} + +static int reg_write(struct isight *isight, int offset, __be32 value) +{ + return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + offset, &value, 4); +} +static void isight_stop_streaming(struct isight *isight) +{ if (!isight->context) return; fw_iso_context_stop(isight->context); fw_iso_context_destroy(isight->context); isight->context = NULL; - - value = 0; - snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_AUDIO_ENABLE, - &value, 4); - fw_iso_resources_free(&isight->resources); + reg_write(isight, REG_AUDIO_ENABLE, 0); } static int isight_hw_free(struct snd_pcm_substream *substream) @@ -345,7 +351,6 @@ static int isight_hw_free(struct snd_pcm_substream *substream) static int isight_start_streaming(struct isight *isight) { - __be32 value; unsigned int i; int err; @@ -356,21 +361,15 @@ static int isight_start_streaming(struct isight *isight) return 0; } - value = cpu_to_be32(RATE_48000); - err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_SAMPLE_RATE, - &value, 4); + err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000)); if (err < 0) - return err; + goto error; err = isight_connect(isight); if (err < 0) goto error; - value = cpu_to_be32(AUDIO_ENABLE); - err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_AUDIO_ENABLE, - &value, 4); + err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE)); if (err < 0) goto err_resources; @@ -407,11 +406,8 @@ err_context: fw_iso_context_destroy(isight->context); isight->context = NULL; err_resources: - value = 0; - snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_AUDIO_ENABLE, - &value, 4); fw_iso_resources_free(&isight->resources); + reg_write(isight, REG_AUDIO_ENABLE, 0); error: return err; } @@ -503,8 +499,7 @@ static int isight_gain_get(struct snd_kcontrol *ctl, __be32 gain; int err; - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_GAIN, &gain, 4); + err = reg_read(isight, REG_GAIN, &gain); if (err < 0) return err; @@ -517,15 +512,13 @@ static int isight_gain_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct isight *isight = ctl->private_data; - __be32 gain; if (value->value.integer.value[0] < isight->gain_min || value->value.integer.value[0] > isight->gain_max) return -EINVAL; - gain = cpu_to_be32(value->value.integer.value[0]); - return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_GAIN, &gain, 4); + return reg_write(isight, REG_GAIN, + cpu_to_be32(value->value.integer.value[0])); } static int isight_mute_get(struct snd_kcontrol *ctl, @@ -535,8 +528,7 @@ static int isight_mute_get(struct snd_kcontrol *ctl, __be32 mute; int err; - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_MUTE, &mute, 4); + err = reg_read(isight, REG_MUTE, &mute); if (err < 0) return err; @@ -549,11 +541,9 @@ static int isight_mute_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct isight *isight = ctl->private_data; - __be32 mute; - mute = (__force __be32)!value->value.integer.value[0]; - return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + REG_MUTE, &mute, 4); + return reg_write(isight, REG_MUTE, + (__force __be32)!value->value.integer.value[0]); } static int isight_create_mixer(struct isight *isight) @@ -578,31 +568,25 @@ static int isight_create_mixer(struct isight *isight) struct snd_kcontrol *ctl; int err; - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_GAIN_RAW_START, - &value, 4); + err = reg_read(isight, REG_GAIN_RAW_START, &value); if (err < 0) return err; isight->gain_min = be32_to_cpu(value); - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_GAIN_RAW_END, - &value, 4); + err = reg_read(isight, REG_GAIN_RAW_END, &value); if (err < 0) return err; isight->gain_max = be32_to_cpu(value); isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; isight->gain_tlv[1] = 2 * sizeof(unsigned int); - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_GAIN_DB_START, - &value, 4); + + err = reg_read(isight, REG_GAIN_DB_START, &value); if (err < 0) return err; isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; - err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + REG_GAIN_DB_END, - &value, 4); + + err = reg_read(isight, REG_GAIN_DB_END, &value); if (err < 0) return err; isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; -- cgit v1.2.3 From aee70400184b6a8d39243b02c244aed61259a46b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:53:12 +0200 Subject: ALSA: isight: fix hang when unplugging a running device When aborting a PCM stream, the xrun is signaled only if the stream is running. When disconnecting a PCM stream, calling snd_card_disconnect() too early would change the stream into a non-running state and thus prevent the xrun from being noticed by user space. To prevent this, move the snd_card_disconnect() call after the xrun. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 4d2edcfdbbc..96267437d37 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -692,10 +692,9 @@ static int isight_remove(struct device *dev) { struct isight *isight = dev_get_drvdata(dev); - snd_card_disconnect(isight->card); - mutex_lock(&isight->mutex); isight_pcm_abort(isight); + snd_card_disconnect(isight->card); isight_stop_streaming(isight); mutex_unlock(&isight->mutex); -- cgit v1.2.3 From 3cabffd72c303c3b5bbbbe88c95b49043898d1f3 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 10:54:41 +0200 Subject: ALSA: isight: remove experimental status Experiments have shown this driver to work now. Signed-off-by: Clemens Ladisch Tested-by: Stefan Richter Signed-off-by: Takashi Iwai --- sound/firewire/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 312f90d7ec6..26071489970 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -23,8 +23,7 @@ config SND_FIREWIRE_SPEAKERS will be called snd-firewire-speakers. config SND_ISIGHT - tristate "Apple iSight microphone (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Apple iSight microphone" select SND_PCM select SND_FIREWIRE_LIB help -- cgit v1.2.3 From f3f7c1837f6bcae3601fc535b339426868bf1549 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 May 2011 11:07:09 +0200 Subject: ALSA: isight: fix locking Lockdep complains about conflicts between isight->mutex, ALSA's register_mutex, mm->mmap_sem, and pcm->open_mutex. This can be fixed by moving the calls to isight_pcm_abort(), snd_card_disconnect(), and fw_iso_resources_update() out of isight->mutex. These functions are designed to be called asynchronously; the mutex needs to protect only the device streaming state modified by isight_start/stop_streaming(). Signed-off-by: Clemens Ladisch Reported-by: Stefan Richter Signed-off-by: Takashi Iwai --- sound/firewire/isight.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 96267437d37..86ee16ca365 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -692,9 +692,11 @@ static int isight_remove(struct device *dev) { struct isight *isight = dev_get_drvdata(dev); - mutex_lock(&isight->mutex); isight_pcm_abort(isight); + snd_card_disconnect(isight->card); + + mutex_lock(&isight->mutex); isight_stop_streaming(isight); mutex_unlock(&isight->mutex); @@ -707,12 +709,13 @@ static void isight_bus_reset(struct fw_unit *unit) { struct isight *isight = dev_get_drvdata(&unit->device); - mutex_lock(&isight->mutex); if (fw_iso_resources_update(&isight->resources) < 0) { isight_pcm_abort(isight); + + mutex_lock(&isight->mutex); isight_stop_streaming(isight); + mutex_unlock(&isight->mutex); } - mutex_unlock(&isight->mutex); } static const struct ieee1394_device_id isight_id_table[] = { -- cgit v1.2.3 From ea27316e4cd13b25727715c0db8adb0b1661f5e7 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 12 May 2011 22:17:56 +0200 Subject: ALSA: tea575x: remove freq_fixup from struct freq_fixup is a constant, no need to hold it in struct snd_tea575x and set in each driver. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 3 ++- sound/i2c/other/tea575x-tuner.c | 8 ++++---- sound/pci/es1968.c | 1 - sound/pci/fm801.c | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index e50cb2934ef..0291f48e035 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -26,6 +26,8 @@ #include #include +#define TEA575X_FMIF 10700 + #define TEA575X_DATA (1 << 0) #define TEA575X_CLK (1 << 1) #define TEA575X_WREN (1 << 2) @@ -46,7 +48,6 @@ struct snd_tea575x { bool mute; /* Device is muted? */ bool stereo; /* receiving stereo */ bool tuned; /* tuned to a station */ - unsigned int freq_fixup; /* crystal onboard */ unsigned int val; /* hw value */ unsigned long freq; /* frequency */ unsigned long in_use; /* set if the device is in use */ diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 31f9795daca..98ccec27e7b 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -141,9 +141,9 @@ static void snd_tea575x_get_freq(struct snd_tea575x *tea) freq /= 10; /* crystal fixup */ if (tea->tea5759) - freq += tea->freq_fixup; + freq += TEA575X_FMIF; else - freq -= tea->freq_fixup; + freq -= TEA575X_FMIF; tea->freq = freq * 16; /* from kHz */ } @@ -156,9 +156,9 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) freq /= 16; /* to kHz */ /* crystal fixup */ if (tea->tea5759) - freq -= tea->freq_fixup; + freq -= TEA575X_FMIF; else - freq += tea->freq_fixup; + freq += TEA575X_FMIF; /* freq /= 12.5 */ freq *= 10; freq /= 125; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 75c5e0ed695..3f3ff1bae81 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2794,7 +2794,6 @@ static int __devinit snd_es1968_create(struct snd_card *card, #ifdef CONFIG_SND_ES1968_RADIO chip->tea.card = card; - chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; chip->tea.ops = &snd_es1968_tea_ops; if (!snd_tea575x_init(&chip->tea)) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 5aa3fd6f25f..1d4e71cc18b 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1231,7 +1231,6 @@ static int __devinit snd_fm801_create(struct snd_card *card, #ifdef TEA575X_RADIO chip->tea.card = card; - chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && -- cgit v1.2.3 From 3d11ba5593b801b1db85e9680d585713e6039112 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 12 May 2011 22:18:09 +0200 Subject: ALSA: tea575x: remove unused card from struct struct snd_card *card is present in struct snd_tea575x but never used. Remove it. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 1 - sound/pci/es1968.c | 1 - sound/pci/fm801.c | 1 - 3 files changed, 3 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 0291f48e035..42ce8cd7616 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -42,7 +42,6 @@ struct snd_tea575x_ops { }; struct snd_tea575x { - struct snd_card *card; struct video_device *vd; /* video device */ bool tea5759; /* 5759 chip is present */ bool mute; /* Device is muted? */ diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 3f3ff1bae81..ec7f8371a81 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2793,7 +2793,6 @@ static int __devinit snd_es1968_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef CONFIG_SND_ES1968_RADIO - chip->tea.card = card; chip->tea.private_data = chip; chip->tea.ops = &snd_es1968_tea_ops; if (!snd_tea575x_init(&chip->tea)) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 1d4e71cc18b..ae5c270bfa7 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1230,7 +1230,6 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef TEA575X_RADIO - chip->tea.card = card; chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && -- cgit v1.2.3 From 10ca72014741554ad37c149ff0d9e93c1e3d5b7d Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 12 May 2011 22:18:22 +0200 Subject: ALSA: tea575x: use better card and bus names Provide real card and bus_info instead of hardcoded values. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- include/sound/tea575x-tuner.h | 2 ++ sound/i2c/other/tea575x-tuner.c | 5 +++-- sound/pci/es1968.c | 2 ++ sound/pci/fm801.c | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 42ce8cd7616..d2ea112fc20 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -52,6 +52,8 @@ struct snd_tea575x { unsigned long in_use; /* set if the device is in use */ struct snd_tea575x_ops *ops; void *private_data; + u8 card[32]; + u8 bus_info[32]; }; int snd_tea575x_init(struct snd_tea575x *tea); diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 98ccec27e7b..4831800239d 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -178,8 +178,9 @@ static int vidioc_querycap(struct file *file, void *priv, struct snd_tea575x *tea = video_drvdata(file); strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); - strlcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757", sizeof(v->card)); - sprintf(v->bus_info, "PCI"); + strlcpy(v->card, tea->card, sizeof(v->card)); + strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); + strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); v->version = RADIO_VERSION; v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index ec7f8371a81..ab0a6156a70 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2795,6 +2795,8 @@ static int __devinit snd_es1968_create(struct snd_card *card, #ifdef CONFIG_SND_ES1968_RADIO chip->tea.private_data = chip; chip->tea.ops = &snd_es1968_tea_ops; + strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card)); + sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); if (!snd_tea575x_init(&chip->tea)) printk(KERN_INFO "es1968: detected TEA575x radio\n"); #endif diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index ae5c270bfa7..05553da2f6c 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1232,6 +1232,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, #ifdef TEA575X_RADIO chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; + sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && (tea575x_tuner & TUNER_TYPE_MASK) < 4) { if (snd_tea575x_init(&chip->tea)) @@ -1246,6 +1247,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, break; } } + strlcpy(chip->tea.card, snd_fm801_tea575x_gpios[(tea575x_tuner & TUNER_TYPE_MASK) - 1].name, sizeof(chip->tea.card)); #endif *rchip = chip; -- cgit v1.2.3 From fdb62b500d27b2a2d6a111769e5c44c1c9f4d909 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sat, 14 May 2011 22:51:01 +0200 Subject: ALSA: fm801: clean-up radio-related Kconfig Remove TEA575X_RADIO define from fm801.c. Also update Kconfig help text to include all supported cards. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 4 ++-- sound/pci/fm801.c | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index f9f533fcccc..ef41825a7b1 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -560,8 +560,8 @@ config SND_FM801_TEA575X_BOOL depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801 help Say Y here to include support for soundcards based on the ForteMedia - FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media - Forte SF256-PCS-02) into the snd-fm801 driver. + FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and + SF64-PCR) into the snd-fm801 driver. config SND_TEA575X tristate diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 05553da2f6c..eacd4901a30 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -38,7 +38,6 @@ #ifdef CONFIG_SND_FM801_TEA575X_BOOL #include -#define TEA575X_RADIO 1 #endif MODULE_AUTHOR("Jaroslav Kysela "); @@ -196,7 +195,7 @@ struct fm801 { spinlock_t reg_lock; struct snd_info_entry *proc_entry; -#ifdef TEA575X_RADIO +#ifdef CONFIG_SND_FM801_TEA575X_BOOL struct snd_tea575x tea; #endif @@ -715,7 +714,7 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc * TEA5757 radio */ -#ifdef TEA575X_RADIO +#ifdef CONFIG_SND_FM801_TEA575X_BOOL /* GPIO to TEA575x maps */ struct snd_fm801_tea575x_gpio { @@ -1150,7 +1149,7 @@ static int snd_fm801_free(struct fm801 *chip) outw(cmdw, FM801_REG(chip, IRQ_MASK)); __end_hw: -#ifdef TEA575X_RADIO +#ifdef CONFIG_SND_FM801_TEA575X_BOOL snd_tea575x_exit(&chip->tea); #endif if (chip->irq >= 0) @@ -1229,7 +1228,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); -#ifdef TEA575X_RADIO +#ifdef CONFIG_SND_FM801_TEA575X_BOOL chip->tea.private_data = chip; chip->tea.ops = &snd_fm801_tea_ops; sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); -- cgit v1.2.3 From 23dc05a33fd45c1f53b51e6ad9a209bf021a16c8 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:38 +0200 Subject: MAINTAINERS: Add entry for Native Instruments sound driver Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8aa1cacddbc..bd21796d947 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4281,6 +4281,13 @@ M: Tim Hockin S: Maintained F: drivers/net/natsemi.c +NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER +M: Daniel Mack +S: Maintained +L: alsa-devel@alsa-project.org +W: http://www.native-instruments.com +F: sound/usb/caiaq/ + NCP FILESYSTEM M: Petr Vandrovec S: Odd Fixes -- cgit v1.2.3 From 60ed286a61b43e3fedf18e63f2d71b758f3505aa Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:39 +0200 Subject: ALSA: usb-audio: make hwc_debug a noop in case HW_CONST_DEBUG is not set Just defining it to nothing is dangerous as it can alter the code execution flow, for example when used in as only function in a conditional code block. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/debug.h b/sound/usb/debug.h index 343ec2d9ee6..58030176f00 100644 --- a/sound/usb/debug.h +++ b/sound/usb/debug.h @@ -8,7 +8,7 @@ #ifdef HW_CONST_DEBUG #define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) #else -#define hwc_debug(fmt, args...) /**/ +#define hwc_debug(fmt, args...) do { } while(0) #endif #endif /* __USBAUDIO_DEBUG_H */ -- cgit v1.2.3 From ee95cb6121dae17bc199cd566503dff1b2dd243b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:40 +0200 Subject: ALSA: usb-audio: include format.h in format.c Just in case a prototype changes, we'll be warned. This also fixes a sparse warning. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/format.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/format.c b/sound/usb/format.c index 5b792d2c806..95d5d7c4f95 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -30,6 +30,7 @@ #include "helper.h" #include "debug.h" #include "clock.h" +#include "format.h" /* * parse the audio format type I descriptor -- cgit v1.2.3 From 759e890f5c25ef087d866b330261e793b03ef7a4 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:41 +0200 Subject: ALSA: usb-audio: remove invalid extra mixers for Komplete Audio 6 This was a flaw in the reading of the spec tables - Native Instrument's "Komplete Audio 6" device has no such extra controls. This patch also fixes the device name in two comments. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 17 ----------------- sound/usb/quirks-table.h | 2 +- sound/usb/quirks.c | 2 +- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4a7ad7ed62f..73dcc8256bc 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -398,17 +398,6 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new snd_nativeinstruments_ak6_mixers[] = { - { - .name = "Direct Monitor Channel 1+2", - .private_value = _MAKE_NI_CONTROL(0x03, 0x03), - }, - { - .name = "Direct Monitor Channel 3+4", - .private_value = _MAKE_NI_CONTROL(0x03, 0x05), - }, -}; - static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { { .name = "Direct Thru Channel A", @@ -537,12 +526,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_xonar_u1_controls_create(mixer); break; - case USB_ID(0x17cc, 0x1001): /* Audio Kontrol 6 */ - err = snd_nativeinstruments_create_mixer(mixer, - snd_nativeinstruments_ak6_mixers, - ARRAY_SIZE(snd_nativeinstruments_ak6_mixers)); - break; - case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 54e18c181a1..5c1a176c426 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2332,7 +2332,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), /* Native Instruments MK2 series */ { - /* Audio Kontrol 6 */ + /* Komplete Audio 6 */ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x17cc, .idProduct = 0x1000, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2452edd2f14..c1a5d7d4580 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -539,7 +539,7 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, /* Access Music VirusTI Desktop */ return snd_usb_accessmusic_boot_quirk(dev); - case USB_ID(0x17cc, 0x1000): /* Audio Kontrol 6 */ + case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */ case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */ case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */ return snd_usb_nativeinstruments_boot_quirk(dev); -- cgit v1.2.3 From 56a9eb1f30c3c7b543a5684e91b47d6ae952feba Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:42 +0200 Subject: ALSA: usb-audio: Add quirk for KORG PANDORA PX5D MIDI interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Mack Reported-and-tested-by: Frédéric Jaume Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 5c1a176c426..78c3e8dbca6 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2179,6 +2179,17 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +/* KORG devices */ +{ + USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "KORG, Inc.", + /* .product_name = "PANDORA PX5D", */ + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE, + } +}, + /* AKAI devices */ { USB_DEVICE(0x09e8, 0x0062), -- cgit v1.2.3 From 0ef283247a0cf0fd2e8370ee467030292eb3129e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:43 +0200 Subject: ALSA: usb-audio: add quirks for Roland GR-55 Signed-off-by: Daniel Mack Reported-by: Jeffrey Scott Flesher Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 78c3e8dbca6..690767c0aad 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1651,6 +1651,32 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE(0x0582, 0x0127), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Roland", */ + /* .product_name = "GR-55", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, /* Guillemot devices */ { -- cgit v1.2.3 From 3bc6fbc7439a88969de97d979795ce7847950668 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:44 +0200 Subject: ALSA: usb-audio: assume valid clock If the interface can't report a clock's validity, assume that it's valid. Signed-off-by: Daniel Mack Reported-by: Vicente Joel Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 7754a103454..075195e8661 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) int err; unsigned char data; struct usb_device *dev = chip->dev; + struct uac_clock_source_descriptor *cs_desc = + snd_usb_find_clock_source(chip->ctrl_intf, source_id); + + if (!cs_desc) + return 0; + + /* If a clock source can't tell us whether it's valid, we assume it is */ + if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID)) + return 1; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, @@ -114,7 +123,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) if (err < 0) { snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n", __func__, source_id); - return err; + return 0; } return !!data; -- cgit v1.2.3 From c91d9cda558fc348205fa972c8b864f8579ef258 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 May 2011 11:28:45 +0200 Subject: ALSA: usb-audio: handle "Fast Track Ultra" with USB_DEVICE_VENDOR_SPEC() That way, the class compliant MIDI interface is also handled. Signed-off-by: Daniel Mack Reported-and-tested-by: Grant Diffey Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 690767c0aad..78792a8900c 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1979,7 +1979,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - USB_DEVICE(0x0763, 0x2080), + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ /* .product_name = "Fast Track Ultra", */ @@ -2046,7 +2046,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - USB_DEVICE(0x0763, 0x2081), + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ /* .product_name = "Fast Track Ultra 8R", */ -- cgit v1.2.3 From 7cdd8d73139ec935a8e91806131a5b91e26c653e Mon Sep 17 00:00:00 2001 From: Mathieu Bouffard Date: Wed, 18 May 2011 17:09:17 +0200 Subject: ALSA: usb-audio - Add support for USB X-Fi S51 Pro USB X-Fi S51 Pro volume and mute from the volume knob on the unit. Compiled and tested with 2.6.39-rc7-git12 Signed-off-by: Mathieu Bouffard Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 73dcc8256bc..9146cffa6ed 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -61,6 +61,7 @@ static const struct rc_config { { USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */ { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */ + { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */ { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ }; @@ -188,6 +189,12 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0, 100); + /* USB X-Fi S51 Pro */ + if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0, 100); else err = snd_usb_ctl_msg(mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, @@ -234,9 +241,13 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) /* USB X-Fi S51 doesn't have a CMSS LED */ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) continue; + /* USB X-Fi S51 Pro doesn't have one either */ + if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0) + continue; if (i > 1 && /* Live24ext has 2 LEDs only */ (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || + mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) break; err = snd_ctl_add(mixer->chip->card, @@ -512,6 +523,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x041e, 0x3020): case USB_ID(0x041e, 0x3040): case USB_ID(0x041e, 0x3042): + case USB_ID(0x041e, 0x30df): case USB_ID(0x041e, 0x3048): err = snd_audigy2nx_controls_create(mixer); if (err < 0) -- cgit v1.2.3 From ec08b14483de0702ca43e3a8506e149486975f9b Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Wed, 18 May 2011 10:03:34 -0400 Subject: ALSA: sound, core, pcm_lib: xrun_log: log also in_interrupt When debugging pcm drivers I found the "period" or "hw" prefix printed by either XRUN_DEBUG_PERIODUPDATE or XRUN_DEBUG_PERIODUPDATE events, respectively to be very useful is observing the interplay between interrupt-context updates and syscall-context updates. Similarly, when debugging overruns with XRUN_DEBUG_LOG it is useful to see the context of the last 10 positions. Add an in_interrupt member to hwptr_log_entry which stores the value of the in_interrupt parameter of snd_pcm_update_hw_ptr0 when the log entry is created. Print a "[Q]" prefix when dumping the log entries if in_interrupt was true. Signed-off-by: Ben Gardiner Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 64449cb8f87..c8c8091f9a7 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -189,6 +189,7 @@ static void xrun(struct snd_pcm_substream *substream) #define XRUN_LOG_CNT 10 struct hwptr_log_entry { + unsigned int in_interrupt; unsigned long jiffies; snd_pcm_uframes_t pos; snd_pcm_uframes_t period_size; @@ -204,7 +205,7 @@ struct snd_pcm_hwptr_log { }; static void xrun_log(struct snd_pcm_substream *substream, - snd_pcm_uframes_t pos) + snd_pcm_uframes_t pos, int in_interrupt) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hwptr_log *log = runtime->hwptr_log; @@ -220,6 +221,7 @@ static void xrun_log(struct snd_pcm_substream *substream, return; } entry = &log->entries[log->idx]; + entry->in_interrupt = in_interrupt; entry->jiffies = jiffies; entry->pos = pos; entry->period_size = runtime->period_size; @@ -246,9 +248,11 @@ static void xrun_log_show(struct snd_pcm_substream *substream) entry = &log->entries[idx]; if (entry->period_size == 0) break; - snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " + snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " "hwptr=%ld/%ld\n", - name, entry->jiffies, (unsigned long)entry->pos, + name, entry->in_interrupt ? "[Q] " : "", + entry->jiffies, + (unsigned long)entry->pos, (unsigned long)entry->period_size, (unsigned long)entry->buffer_size, (unsigned long)entry->old_hw_ptr, @@ -326,7 +330,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } pos -= pos % runtime->min_align; if (xrun_debug(substream, XRUN_DEBUG_LOG)) - xrun_log(substream, pos); + xrun_log(substream, pos, in_interrupt); hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; if (in_interrupt) { -- cgit v1.2.3 From 217658f46c2dbfe260f8f5976f2a201911a2f4c6 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Wed, 18 May 2011 23:52:38 -0400 Subject: ALSA: sound, core, pcm_lib: fix xrun_log The xrun_log function was augmented with the in_interrupt parameter whereas the empty macro definition used when xrun logging is disabled was not. Add a third parameter to the empty macro definition so as to not cause compiler errors when xrun logging (CONFIG_SND_PCM_XRUN_DEBUG) is disabled. Signed-off-by: Ben Gardiner Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index c8c8091f9a7..abfeff1611c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -266,7 +266,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream) #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ #define hw_ptr_error(substream, fmt, args...) do { } while (0) -#define xrun_log(substream, pos) do { } while (0) +#define xrun_log(substream, pos, in_interrupt) do { } while (0) #define xrun_log_show(substream) do { } while (0) #endif -- cgit v1.2.3 From df1fe13289eca445b77471c204408bec67313be3 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Thu, 19 May 2011 18:48:27 +0400 Subject: ALSA: intel8x0m: enable AMD8111 modem AMD 8111 southbridges contain a controller for MC'97 modem. Enable support for this controller in intel8x0m driver. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Takashi Iwai --- sound/pci/intel8x0m.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 2ae8d29500a..bae3a279f75 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -235,8 +235,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = { { PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */ { PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */ { PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */ + { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */ #if 0 - { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */ { PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */ #endif { 0, } @@ -1261,9 +1261,9 @@ static struct shortname_table { { PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" }, { PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" }, { PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" }, + { 0x746e, "AMD AMD8111" }, #if 0 { 0x5455, "ALi M5455" }, - { 0x746d, "AMD AMD8111" }, #endif { 0 }, }; -- cgit v1.2.3 From bfe9fc8aebc997ce8bcf8ac0586c84a247812064 Mon Sep 17 00:00:00 2001 From: Raymond Yau Date: Fri, 20 May 2011 14:32:04 +0800 Subject: ALSA: emu10k1 - Add dB range to Bass and Treble for SB Live! As the "Wave", "Wave Surround" or "Front" Playback Volume must be changed to 70% (i.e. -12 dB) so that distortion won't occur when increase Bass and Treble from 50% to 100%, so the maximum gain in Bass and Treble are +12 dB. Signed-off-by: Raymond Yau Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emufx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 7a9401462c1..dae4050ede5 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -303,6 +303,9 @@ static const u32 db_table[101] = { static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1); static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0); +/* EMU10K1 bass/treble db gain */ +static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0); + static const u32 onoff_table[2] = { 0x00000000, 0x00000001 }; @@ -2163,6 +2166,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->min = 0; ctl->max = 40; ctl->value[0] = ctl->value[1] = 20; + ctl->tlv = snd_emu10k1_bass_treble_db_scale; ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -2172,6 +2176,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->min = 0; ctl->max = 40; ctl->value[0] = ctl->value[1] = 20; + ctl->tlv = snd_emu10k1_bass_treble_db_scale; ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE; #define BASS_GPR 0x8c -- cgit v1.2.3