aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/ice1712/ice1712.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/ice1712/ice1712.c')
-rw-r--r--sound/pci/ice1712/ice1712.c2760
1 files changed, 2760 insertions, 0 deletions
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
new file mode 100644
index 00000000000..79fba6be350
--- /dev/null
+++ b/sound/pci/ice1712/ice1712.c
@@ -0,0 +1,2760 @@
+/*
+ * ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ NOTES:
+ - spdif nonaudio consumer mode does not work (at least with my
+ Sony STR-DB830)
+*/
+
+/*
+ * Changes:
+ *
+ * 2002.09.09 Takashi Iwai <tiwai@suse.de>
+ * split the code to several files. each low-level routine
+ * is stored in the local file and called from registration
+ * function from card_info struct.
+ *
+ * 2002.11.26 James Stafford <jstafford@ampltd.com>
+ * Added support for VT1724 (Envy24HT)
+ * I have left out support for 176.4 and 192 KHz for the moment.
+ * I also haven't done anything with the internal S/PDIF transmitter or the MPU-401
+ *
+ * 2003.02.20 Taksahi Iwai <tiwai@suse.de>
+ * Split vt1724 part to an independent driver.
+ * The GPIO is accessed through the callback functions now.
+ *
+ * 2004.03.31 Doug McLain <nostar@comcast.net>
+ * Added support for Event Electronics EZ8 card to hoontech.c.
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#include <sound/initval.h>
+
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+
+/* lowlevel routines */
+#include "delta.h"
+#include "ews.h"
+#include "hoontech.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{"
+ HOONTECH_DEVICE_DESC
+ DELTA_DEVICE_DESC
+ EWS_DEVICE_DESC
+ "{ICEnsemble,Generic ICE1712},"
+ "{ICEnsemble,Generic Envy24}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static char *model[SNDRV_CARDS];
+static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
+static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard.");
+module_param_array(omni, bool, NULL, 0444);
+MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support.");
+module_param_array(cs8427_timeout, int, NULL, 0444);
+MODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE 0x1412
+#endif
+#ifndef PCI_DEVICE_ID_ICE_1712
+#define PCI_DEVICE_ID_ICE_1712 0x1712
+#endif
+
+static struct pci_device_id snd_ice1712_ids[] = {
+ { PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICE1712 */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
+
+static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
+static int snd_ice1712_build_controls(ice1712_t *ice);
+
+static int PRO_RATE_LOCKED;
+static int PRO_RATE_RESET = 1;
+static unsigned int PRO_RATE_DEFAULT = 44100;
+
+/*
+ * Basic I/O
+ */
+
+/* check whether the clock mode is spdif-in */
+static inline int is_spdif_master(ice1712_t *ice)
+{
+ return (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER) ? 1 : 0;
+}
+
+static inline int is_pro_rate_locked(ice1712_t *ice)
+{
+ return is_spdif_master(ice) || PRO_RATE_LOCKED;
+}
+
+static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
+{
+ outb((channel << 4) | addr, ICEDS(ice, INDEX));
+ outl(data, ICEDS(ice, DATA));
+}
+
+static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
+{
+ outb((channel << 4) | addr, ICEDS(ice, INDEX));
+ return inl(ICEDS(ice, DATA));
+}
+
+static void snd_ice1712_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEREG(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEREG(ice, AC97_INDEX));
+ outw(val, ICEREG(ice, AC97_DATA));
+ old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+ outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+ break;
+}
+
+static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEREG(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEREG(ice, AC97_INDEX));
+ outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+ break;
+ if (tm >= 0x10000) /* timeout */
+ return ~0;
+ return inw(ICEREG(ice, AC97_DATA));
+}
+
+/*
+ * pro ac97 section
+ */
+
+static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
+ unsigned short reg,
+ unsigned short val)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEMT(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEMT(ice, AC97_INDEX));
+ outw(val, ICEMT(ice, AC97_DATA));
+ old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+ outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+ break;
+}
+
+
+static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
+ unsigned short reg)
+{
+ ice1712_t *ice = (ice1712_t *)ac97->private_data;
+ int tm;
+ unsigned char old_cmd = 0;
+
+ for (tm = 0; tm < 0x10000; tm++) {
+ old_cmd = inb(ICEMT(ice, AC97_CMD));
+ if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+ continue;
+ if (!(old_cmd & ICE1712_AC97_READY))
+ continue;
+ break;
+ }
+ outb(reg, ICEMT(ice, AC97_INDEX));
+ outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
+ for (tm = 0; tm < 0x10000; tm++)
+ if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+ break;
+ if (tm >= 0x10000) /* timeout */
+ return ~0;
+ return inw(ICEMT(ice, AC97_DATA));
+}
+
+/*
+ * consumer ac97 digital mix
+ */
+static int snd_ice1712_digmix_route_ac97_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
+ return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+ ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+ unsigned char val, nval;
+
+ spin_lock_irq(&ice->reg_lock);
+ val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
+ nval = val & ~ICE1712_ROUTE_AC97;
+ if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+ outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
+ spin_unlock_irq(&ice->reg_lock);
+ return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Mixer To AC97",
+ .info = snd_ice1712_digmix_route_ac97_info,
+ .get = snd_ice1712_digmix_route_ac97_get,
+ .put = snd_ice1712_digmix_route_ac97_put,
+};
+
+
+/*
+ * gpio operations
+ */
+static void snd_ice1712_set_gpio_dir(ice1712_t *ice, unsigned int data)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, data);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+static void snd_ice1712_set_gpio_mask(ice1712_t *ice, unsigned int data)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+static unsigned int snd_ice1712_get_gpio_data(ice1712_t *ice)
+{
+ return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+}
+
+static void snd_ice1712_set_gpio_data(ice1712_t *ice, unsigned int val)
+{
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, val);
+ inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
+}
+
+
+/*
+ *
+ * CS8427 interface
+ *
+ */
+
+/*
+ * change the input clock selection
+ * spdif_clock = 1 - IEC958 input, 0 - Envy24
+ */
+static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
+{
+ unsigned char reg[2] = { 0x80 | 4, 0 }; /* CS8427 auto increment | register number 4 + data */
+ unsigned char val, nval;
+ int res = 0;
+
+ snd_i2c_lock(ice->i2c);
+ if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ return -EIO;
+ }
+ nval = val & 0xf0;
+ if (spdif_clock)
+ nval |= 0x01;
+ else
+ nval |= 0x04;
+ if (val != nval) {
+ reg[1] = nval;
+ if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
+ res = -EIO;
+ } else {
+ res++;
+ }
+ }
+ snd_i2c_unlock(ice->i2c);
+ return res;
+}
+
+/*
+ * spdif callbacks
+ */
+static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ snd_cs8427_iec958_active(ice->cs8427, 1);
+}
+
+static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+ snd_cs8427_iec958_active(ice->cs8427, 0);
+}
+
+static void setup_cs8427(ice1712_t *ice, int rate)
+{
+ snd_cs8427_iec958_pcm(ice->cs8427, rate);
+}
+
+/*
+ * create and initialize callbacks for cs8427 interface
+ */
+int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr)
+{
+ int err;
+
+ if ((err = snd_cs8427_create(ice->i2c, addr,
+ (ice->cs8427_timeout * HZ) / 1000,
+ &ice->cs8427)) < 0) {
+ snd_printk("CS8427 initialization failed\n");
+ return err;
+ }
+ ice->spdif.ops.open = open_cs8427;
+ ice->spdif.ops.close = close_cs8427;
+ ice->spdif.ops.setup_rate = setup_cs8427;
+ return 0;
+}
+
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ice1712_t *ice = dev_id;
+ unsigned char status;
+ int handled = 0;
+
+ while (1) {
+ status = inb(ICEREG(ice, IRQSTAT));
+ if (status == 0)
+ break;
+ handled = 1;
+ if (status & ICE1712_IRQ_MPU1) {
+ if (ice->rmidi[0])
+ snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+ outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
+ status &= ~ICE1712_IRQ_MPU1;
+ }
+ if (status & ICE1712_IRQ_TIMER)
+ outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
+ if (status & ICE1712_IRQ_MPU2) {
+ if (ice->rmidi[1])
+ snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
+ outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
+ status &= ~ICE1712_IRQ_MPU2;
+ }
+ if (status & ICE1712_IRQ_PROPCM) {
+ unsigned char mtstat = inb(ICEMT(ice, IRQ));
+ if (mtstat & ICE1712_MULTI_PBKSTATUS) {
+ if (ice->playback_pro_substream)
+ snd_pcm_period_elapsed(ice->playback_pro_substream);
+ outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
+ }
+ if (mtstat & ICE1712_MULTI_CAPSTATUS) {
+ if (ice->capture_pro_substream)
+ snd_pcm_period_elapsed(ice->capture_pro_substream);
+ outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
+ }
+ }
+ if (status & ICE1712_IRQ_FM)
+ outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
+ if (status & ICE1712_IRQ_PBKDS) {
+ u32 idx;
+ u16 pbkstatus;
+ snd_pcm_substream_t *substream;
+ pbkstatus = inw(ICEDS(ice, INTSTAT));
+ //printk("pbkstatus = 0x%x\n", pbkstatus);
+ for (idx = 0; idx < 6; idx++) {
+ if ((pbkstatus & (3 << (idx * 2))) == 0)
+ continue;
+ if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+ snd_pcm_period_elapsed(substream);
+ outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
+ }
+ outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
+ }
+ if (status & ICE1712_IRQ_CONCAP) {
+ if (ice->capture_con_substream)
+ snd_pcm_period_elapsed(ice->capture_con_substream);
+ outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
+ }
+ if (status & ICE1712_IRQ_CONPBK) {
+ if (ice->playback_con_substream)
+ snd_pcm_period_elapsed(ice->playback_con_substream);
+ outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * PCM part - misc
+ */
+
+static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * PCM part - consumer I/O
+ */
+
+static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u32 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+ tmp |= 2;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+ tmp &= ~2;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u32 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+ tmp |= 2;
+ } else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+ tmp &= ~2;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ int result = 0;
+ u8 tmp;
+
+ spin_lock(&ice->reg_lock);
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ tmp |= 1;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ tmp &= ~1;
+ } else {
+ result = -EINVAL;
+ }
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ spin_unlock(&ice->reg_lock);
+ return result;
+}
+
+static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size, rate, tmp;
+
+ period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x0000;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp |= 0x10;
+ if (runtime->channels == 2)
+ tmp |= 0x08;
+ rate = (runtime->rate * 8192) / 375;
+ if (rate > 0x000fffff)
+ rate = 0x000fffff;
+ spin_lock_irq(&ice->reg_lock);
+ outb(0, ice->ddma_port + 15);
+ outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
+ outl(runtime->dma_addr, ice->ddma_port + 0);
+ outw(buf_size, ice->ddma_port + 4);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
+ snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size, rate, tmp, chn;
+
+ period_size = snd_pcm_lib_period_bytes(substream) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x0064;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp &= ~0x04;
+ if (runtime->channels == 2)
+ tmp |= 0x08;
+ rate = (runtime->rate * 8192) / 375;
+ if (rate > 0x000fffff)
+ rate = 0x000fffff;
+ ice->playback_con_active_buf[substream->number] = 0;
+ ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
+ chn = substream->number * 2;
+ spin_lock_irq(&ice->reg_lock);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
+ snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
+ if (runtime->channels == 2) {
+ snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
+ snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
+ }
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ u32 period_size, buf_size;
+ u8 tmp;
+
+ period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+ buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+ tmp = 0x06;
+ if (snd_pcm_format_width(runtime->format) == 16)
+ tmp &= ~0x04;
+ if (runtime->channels == 2)
+ tmp &= ~0x02;
+ spin_lock_irq(&ice->reg_lock);
+ outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+ outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ spin_unlock_irq(&ice->reg_lock);
+ snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ size_t ptr;
+
+ if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
+ return 0;
+ ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+ if (ptr == runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u8 addr;
+ size_t ptr;
+
+ if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
+ return 0;
+ if (ice->playback_con_active_buf[substream->number])
+ addr = ICE1712_DSC_ADDR1;
+ else
+ addr = ICE1712_DSC_ADDR0;
+ ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
+ ice->playback_con_virt_addr[substream->number];
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
+ return 0;
+ ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (64*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (128*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (64*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_con_substream = substream;
+ runtime->hw = snd_ice1712_playback;
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u32 tmp;
+
+ ice->playback_con_substream_ds[substream->number] = substream;
+ runtime->hw = snd_ice1712_playback_ds;
+ spin_lock_irq(&ice->reg_lock);
+ tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
+ outw(tmp, ICEDS(ice, INTMASK));
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_con_substream = substream;
+ runtime->hw = snd_ice1712_capture;
+ runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
+ if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+ runtime->hw.rate_min = 48000;
+ return 0;
+}
+
+static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_con_substream = NULL;
+ return 0;
+}
+
+static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ u32 tmp;
+
+ spin_lock_irq(&ice->reg_lock);
+ tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
+ outw(tmp, ICEDS(ice, INTMASK));
+ spin_unlock_irq(&ice->reg_lock);
+ ice->playback_con_substream_ds[substream->number] = NULL;
+ return 0;
+}
+
+static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_con_substream = NULL;
+ return 0;
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+ .open = snd_ice1712_playback_open,
+ .close = snd_ice1712_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_playback_prepare,
+ .trigger = snd_ice1712_playback_trigger,
+ .pointer = snd_ice1712_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+ .open = snd_ice1712_playback_ds_open,
+ .close = snd_ice1712_playback_ds_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_playback_ds_prepare,
+ .trigger = snd_ice1712_playback_ds_trigger,
+ .pointer = snd_ice1712_playback_ds_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+ .open = snd_ice1712_capture_open,
+ .close = snd_ice1712_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_ice1712_hw_params,
+ .hw_free = snd_ice1712_hw_free,
+ .prepare = snd_ice1712_capture_prepare,
+ .trigger = snd_ice1712_capture_trigger,
+ .pointer = snd_ice1712_capture_pointer,
+};
+
+static void snd_ice1712_pcm_free(snd_pcm_t *pcm)
+{
+ ice1712_t *ice = pcm->private_data;
+ ice->pcm = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
+
+ pcm->private_data = ice;
+ pcm->private_free = snd_ice1712_pcm_free;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1712 consumer");
+ ice->pcm = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+
+ return 0;
+}
+
+static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
+{
+ ice1712_t *ice = pcm->private_data;
+ ice->pcm_ds = NULL;
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
+
+ pcm->private_data = ice;
+ pcm->private_free = snd_ice1712_pcm_free_ds;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "ICE1712 consumer (DS)");
+ ice->pcm_ds = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(ice->pci), 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+
+ return 0;
+}
+
+/*
+ * PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000 };
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ {
+ unsigned int what;
+ unsigned int old;
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+ what = ICE1712_PLAYBACK_PAUSE;
+ snd_pcm_trigger_done(substream, substream);
+ spin_lock(&ice->reg_lock);
+ old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ old |= what;
+ else
+ old &= ~what;
+ outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+ spin_unlock(&ice->reg_lock);
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ unsigned int what = 0;
+ unsigned int old;
+ struct list_head *pos;
+ snd_pcm_substream_t *s;
+
+ snd_pcm_group_for_each(pos, substream) {
+ s = snd_pcm_group_substream_entry(pos);
+ if (s == ice->playback_pro_substream) {
+ what |= ICE1712_PLAYBACK_START;
+ snd_pcm_trigger_done(s, substream);
+ } else if (s == ice->capture_pro_substream) {
+ what |= ICE1712_CAPTURE_START_SHADOW;
+ snd_pcm_trigger_done(s, substream);
+ }
+ }
+ spin_lock(&ice->reg_lock);
+ old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ old |= what;
+ else
+ old &= ~what;
+ outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+ spin_unlock(&ice->reg_lock);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ */
+static void snd_ice1712_set_pro_rate(ice1712_t *ice, unsigned int rate, int force)
+{
+ unsigned long flags;
+ unsigned char val, old;
+ unsigned int i;
+
+ switch (rate) {
+ case 8000: val = 6; break;
+ case 9600: val = 3; break;
+ case 11025: val = 10; break;
+ case 12000: val = 2; break;
+ case 16000: val = 5; break;
+ case 22050: val = 9; break;
+ case 24000: val = 1; break;
+ case 32000: val = 4; break;
+ case 44100: val = 8; break;
+ case 48000: val = 0; break;
+ case 64000: val = 15; break;
+ case 88200: val = 11; break;
+ case 96000: val = 7; break;
+ default:
+ snd_BUG();
+ val = 0;
+ rate = 48000;
+ break;
+ }
+
+ spin_lock_irqsave(&ice->reg_lock, flags);
+ if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+ ICE1712_PLAYBACK_PAUSE|
+ ICE1712_PLAYBACK_START)) {
+ __out:
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+ return;
+ }
+ if (!force && is_pro_rate_locked(ice))
+ goto __out;
+
+ old = inb(ICEMT(ice, RATE));
+ if (!force && old == val)
+ goto __out;
+ outb(val, ICEMT(ice, RATE));
+ spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+ if (ice->gpio.set_pro_rate)
+ ice->gpio.set_pro_rate(ice, rate);
+ for (i = 0; i < ice->akm_codecs; i++) {
+ if (ice->akm[i].ops.set_rate_val)
+ ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
+ }
+ if (ice->spdif.ops.setup_rate)
+ ice->spdif.ops.setup_rate(ice, rate);
+}
+
+static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
+ spin_lock_irq(&ice->reg_lock);
+ outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
+ outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
+ outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
+ spin_unlock_irq(&ice->reg_lock);
+
+ return 0;
+}
+
+static int snd_ice1712_playback_pro_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
+ spin_lock_irq(&ice->reg_lock);
+ outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
+ outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
+ outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
+ spin_unlock_irq(&ice->reg_lock);
+ return 0;
+}
+
+static int snd_ice1712_capture_pro_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+ snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
+ return 0;
+ ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+ if (ptr == substream->runtime->buffer_size)
+ ptr = 0;
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
+{
+ ice1712_t *ice = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))