From 6a40dc5ab5036722d8102ba7190dbd9d72982637 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 13 Oct 2014 11:37:18 +0530 Subject: ALSA: au88x0: added reference of vortex_t added a pointer of the vortex in the following functions : vortex_alsafmt_aspfmt vortex_Vort3D_InitializeSource a3dsrc_ZeroStateA3D so that we can have a reference of the vortex in the function. this reference of the vortex will actually be used in a later patch to convert the pr_* macro to dev_*. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 466a5c8..3a8fefe 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -243,7 +243,7 @@ static int vortex_core_init(vortex_t * card); static int vortex_core_shutdown(vortex_t * card); static void vortex_enable_int(vortex_t * card); static irqreturn_t vortex_interrupt(int irq, void *dev_id); -static int vortex_alsafmt_aspfmt(int alsafmt); +static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v); /* Connection stuff. */ static void vortex_connect_default(vortex_t * vortex, int en); @@ -278,7 +278,7 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix, static void vortex_Vort3D_enable(vortex_t * v); static void vortex_Vort3D_disable(vortex_t * v); static void vortex_Vort3D_connect(vortex_t * vortex, int en); -static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en); +static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v); #endif /* Driver stuff. */ diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c index 30f760e..bc9cda3a 100644 --- a/sound/pci/au88x0/au88x0_a3d.c +++ b/sound/pci/au88x0/au88x0_a3d.c @@ -484,7 +484,7 @@ static void a3dsrc_ZeroState(a3dsrc_t * a) } /* Reset entire A3D engine */ -static void a3dsrc_ZeroStateA3D(a3dsrc_t * a) +static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v) { int i, var, var2; @@ -601,7 +601,7 @@ static void vortex_Vort3D_enable(vortex_t *v) Vort3DRend_Initialize(v, XT_HEADPHONE); for (i = 0; i < NR_A3D; i++) { vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2); - a3dsrc_ZeroStateA3D(&(v->a3d[0])); + a3dsrc_ZeroStateA3D(&v->a3d[0], v); } /* Register ALSA controls */ vortex_a3d_register_controls(v); @@ -676,7 +676,7 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) } /* Initialize one single A3D source. */ -static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en) +static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v) { if (a->vortex == NULL) { pr_warn diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 72e8128..00e2096 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2177,7 +2177,8 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, return -EBUSY; } /* (De)Initialize A3D hardware source. */ - vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en); + vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en, + vortex); } /* Make SPDIF out exclusive to "spdif" device when in use. */ if ((stream->type == VORTEX_PCM_SPDIF) && (en)) { @@ -2765,7 +2766,7 @@ static int vortex_core_shutdown(vortex_t * vortex) /* Alsa support. */ -static int vortex_alsafmt_aspfmt(int alsafmt) +static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v) { int fmt; diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 5adc6b9..bdde182 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -332,7 +332,7 @@ static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream) dir = 1; else dir = 0; - fmt = vortex_alsafmt_aspfmt(runtime->format); + fmt = vortex_alsafmt_aspfmt(runtime->format, chip); spin_lock_irq(&chip->lock); if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { vortex_adbdma_setmode(chip, dma, 1, dir, fmt, -- cgit v0.10.2 From 70c84418bf74f582e29906f1eeb19f2e9da53ddd Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 13 Oct 2014 11:37:19 +0530 Subject: ALSA: au88x0: pr_* replaced with dev_* pr_* macros replaced with dev_* as they are more preffered over pr_*. each file which had pr_* was reviewed manually and replaced with dev_*. here we have actually used the reference of the vortex which was added to some functions in the previous patch of this series. The prefix of the CARD_NAME and prefix of "vortex:" was also removed as the dev_* will print the device name. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 21ce31f..e9c3833 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -48,11 +48,10 @@ static void vortex_fix_latency(struct pci_dev *vortex) { int rc; if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) { - pr_info( CARD_NAME - ": vortex latency is 0xff\n"); + dev_info(&vortex->dev, "vortex latency is 0xff\n"); } else { - pr_warn( CARD_NAME - ": could not set vortex latency: pci error 0x%x\n", rc); + dev_warn(&vortex->dev, + "could not set vortex latency: pci error 0x%x\n", rc); } } @@ -70,11 +69,10 @@ static void vortex_fix_agp_bridge(struct pci_dev *via) if (!(rc = pci_read_config_byte(via, 0x42, &value)) && ((value & 0x10) || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) { - pr_info( CARD_NAME - ": bridge config is 0x%x\n", value | 0x10); + dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10); } else { - pr_warn( CARD_NAME - ": could not set vortex latency: pci error 0x%x\n", rc); + dev_warn(&via->dev, + "could not set vortex latency: pci error 0x%x\n", rc); } } @@ -97,7 +95,8 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix) PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); } if (via) { - pr_info( CARD_NAME ": Activating latency workaround...\n"); + dev_info(&vortex->dev, + "Activating latency workaround...\n"); vortex_fix_latency(vortex); vortex_fix_agp_bridge(via); } @@ -153,7 +152,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) return err; if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { - pr_err( "error to set DMA mask\n"); + dev_err(card->dev, "error to set DMA mask\n"); pci_disable_device(pci); return -ENXIO; } @@ -182,7 +181,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) chip->mmio = pci_ioremap_bar(pci, 0); if (!chip->mmio) { - pr_err( "MMIO area remap failed.\n"); + dev_err(card->dev, "MMIO area remap failed.\n"); err = -ENOMEM; goto ioremap_out; } @@ -191,14 +190,14 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) * This must be done before we do request_irq otherwise we can get spurious * interrupts that we do not handle properly and make a mess of things */ if ((err = vortex_core_init(chip)) != 0) { - pr_err( "hw core init failed\n"); + dev_err(card->dev, "hw core init failed\n"); goto core_out; } if ((err = request_irq(pci->irq, vortex_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) != 0) { - pr_err( "cannot grab irq\n"); + dev_err(card->dev, "cannot grab irq\n"); goto irq_out; } chip->irq = pci->irq; @@ -342,11 +341,11 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) chip->rev = pci->revision; #ifdef CHIP_AU8830 if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { - pr_alert( - "vortex: The revision (%x) of your card has not been seen before.\n", + dev_alert(card->dev, + "The revision (%x) of your card has not been seen before.\n", chip->rev); - pr_alert( - "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); + dev_alert(card->dev, + "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); snd_card_free(card); err = -ENODEV; return err; diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c index bc9cda3a..ab0f873 100644 --- a/sound/pci/au88x0/au88x0_a3d.c +++ b/sound/pci/au88x0/au88x0_a3d.c @@ -489,7 +489,8 @@ static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v) int i, var, var2; if ((a->vortex) == NULL) { - pr_err( "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n"); + dev_err(v->card->dev, + "ZeroStateA3D: ERROR: a->vortex is NULL\n"); return; } @@ -628,15 +629,15 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) v->mixxtlk[0] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[0] < 0) { - pr_warn - ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); + dev_warn(v->card->dev, + "vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } v->mixxtlk[1] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[1] < 0) { - pr_warn - ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); + dev_warn(v->card->dev, + "vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } #endif @@ -679,8 +680,8 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v) { if (a->vortex == NULL) { - pr_warn - ("vortex: Vort3D_InitializeSource: A3D source not initialized\n"); + dev_warn(v->card->dev, + "Vort3D_InitializeSource: A3D source not initialized\n"); return; } if (en) { diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 00e2096..4667c32 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -285,8 +285,8 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - pr_err( - "vortex_mixer_addWTD: lifeboat overflow\n"); + dev_err(vortex->card->dev, + "vortex_mixer_addWTD: lifeboat overflow\n"); return 0; } } @@ -303,7 +303,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_MIXER_SR); if (((1 << ch) & eax) == 0) { - pr_err( "mix ALARM %x\n", eax); + dev_err(vortex->card->dev, "mix ALARM %x\n", eax); return 0; } ebp = VORTEX_MIXER_CHNBASE + (ch << 2); @@ -324,8 +324,8 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) //printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != mix) { if ((esi) > 0xf) { - pr_err( - "vortex: mixdelWTD: error lifeboat overflow\n"); + dev_err(vortex->card->dev, + "mixdelWTD: error lifeboat overflow\n"); return 0; } esp14 = ebx; @@ -492,7 +492,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio) hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio); temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2)); if ((++lifeboat) > 0x9) { - pr_err( "Vortex: Src cvr fail\n"); + dev_err(vortex->card->dev, "Src cvr fail\n"); break; } } @@ -684,8 +684,8 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - pr_err( - "vortex_src_addWTD: lifeboat overflow\n"); + dev_err(vortex->card->dev, + "vortex_src_addWTD: lifeboat overflow\n"); return 0; } } @@ -703,7 +703,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR); if (((1 << ch) & eax) == 0) { - pr_err( "src alarm\n"); + dev_err(vortex->card->dev, "src alarm\n"); return 0; } ebp = VORTEX_SRC_CHNBASE + (ch << 2); @@ -724,8 +724,8 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) //printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != src) { if ((esi) > 0xf) { - pr_warn - ("vortex: srcdelWTD: error, lifeboat overflow\n"); + dev_warn(vortex->card->dev, + "srcdelWTD: error, lifeboat overflow\n"); return 0; } esp14 = ebx; @@ -819,8 +819,8 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - pr_err( - "Vortex: vortex_fifo_setadbctrl fail\n"); + dev_err(vortex->card->dev, + "vortex_fifo_setadbctrl fail\n"); break; } } @@ -915,7 +915,8 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - pr_err( "Vortex: vortex_fifo_setwtctrl fail\n"); + dev_err(vortex->card->dev, + "vortex_fifo_setwtctrl fail\n"); break; } } @@ -1042,7 +1043,7 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_ADB - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1)); if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1)) - pr_err( "bad adb fifo reset!"); + dev_err(vortex->card->dev, "bad adb fifo reset!"); vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE); addr -= 4; } @@ -1053,9 +1054,9 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_WT - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, FIFO_U0); if (hwread(vortex->mmio, addr) != FIFO_U0) - pr_err( - "bad wt fifo reset (0x%08x, 0x%08x)!\n", - addr, hwread(vortex->mmio, addr)); + dev_err(vortex->card->dev, + "bad wt fifo reset (0x%08x, 0x%08x)!\n", + addr, hwread(vortex->mmio, addr)); vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE); addr -= 4; } @@ -1213,8 +1214,9 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma) if (dma->period_virt >= dma->nr_periods) dma->period_virt -= dma->nr_periods; if (delta != 1) - pr_info( "vortex: %d virt=%d, real=%d, delta=%d\n", - adbdma, dma->period_virt, dma->period_real, delta); + dev_info(vortex->card->dev, + "%d virt=%d, real=%d, delta=%d\n", + adbdma, dma->period_virt, dma->period_real, delta); return delta; } @@ -1482,8 +1484,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma) dma->period_real = page; if (delta != 1) - pr_warn( "vortex: wt virt = %d, delta = %d\n", - dma->period_virt, delta); + dev_warn(vortex->card->dev, "wt virt = %d, delta = %d\n", + dma->period_virt, delta); return delta; } @@ -1667,9 +1669,9 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK; if ((lifeboat++) > ADB_MASK) { - pr_err( - "vortex_adb_addroutes: unending route! 0x%x\n", - *route); + dev_err(vortex->card->dev, + "vortex_adb_addroutes: unending route! 0x%x\n", + *route); return; } } @@ -1703,9 +1705,9 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK; if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) { - pr_err( - "vortex_adb_delroutes: route not found! 0x%x\n", - route0); + dev_err(vortex->card->dev, + "vortex_adb_delroutes: route not found! 0x%x\n", + route0); return; } } @@ -2045,7 +2047,9 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) } } } - pr_err( "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype); + dev_err(vortex->card->dev, + "FATAL: ResManager: resource type %d exhausted.\n", + restype); return -ENOMEM; } @@ -2173,7 +2177,8 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, memset(stream->resources, 0, sizeof(unsigned char) * VORTEX_RESOURCE_LAST); - pr_err( "vortex: out of A3D sources. Sorry\n"); + dev_err(vortex->card->dev, + "out of A3D sources. Sorry\n"); return -EBUSY; } /* (De)Initialize A3D hardware source. */ @@ -2422,7 +2427,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) hwread(vortex->mmio, VORTEX_IRQ_SOURCE); // Is at least one IRQ flag set? if (source == 0) { - pr_err( "vortex: missing irq source\n"); + dev_err(vortex->card->dev, "missing irq source\n"); return IRQ_NONE; } @@ -2430,19 +2435,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) // Attend every interrupt source. if (unlikely(source & IRQ_ERR_MASK)) { if (source & IRQ_FATAL) { - pr_err( "vortex: IRQ fatal error\n"); + dev_err(vortex->card->dev, "IRQ fatal error\n"); } if (source & IRQ_PARITY) { - pr_err( "vortex: IRQ parity error\n"); + dev_err(vortex->card->dev, "IRQ parity error\n"); } if (source & IRQ_REG) { - pr_err( "vortex: IRQ reg error\n"); + dev_err(vortex->card->dev, "IRQ reg error\n"); } if (source & IRQ_FIFO) { - pr_err( "vortex: IRQ fifo error\n"); + dev_err(vortex->card->dev, "IRQ fifo error\n"); } if (source & IRQ_DMA) { - pr_err( "vortex: IRQ dma error\n"); + dev_err(vortex->card->dev, "IRQ dma error\n"); } handled = 1; } @@ -2490,7 +2495,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) } if (!handled) { - pr_err( "vortex: unknown irq source %x\n", source); + dev_err(vortex->card->dev, "unknown irq source %x\n", source); } return IRQ_RETVAL(handled); } @@ -2547,7 +2552,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 codec stuck busy\n"); + dev_err(card->card->dev, "ac97 codec stuck busy\n"); return; } } @@ -2573,7 +2578,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 codec stuck busy\n"); + dev_err(card->card->dev, "ac97 codec stuck busy\n"); return 0xffff; } } @@ -2587,7 +2592,8 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short udelay(100); data = hwread(card->mmio, VORTEX_CODEC_IO); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 address never arrived\n"); + dev_err(card->card->dev, + "ac97 address never arrived\n"); return 0xffff; } } while ((data & VORTEX_CODEC_ADDMASK) != @@ -2684,7 +2690,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode) static int vortex_core_init(vortex_t *vortex) { - pr_info( "Vortex: init.... "); + dev_info(vortex->card->dev, "init started\n"); /* Hardware Init. */ hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff); msleep(5); @@ -2729,7 +2735,7 @@ static int vortex_core_init(vortex_t *vortex) //vortex_enable_timer_int(vortex); //vortex_disable_timer_int(vortex); - pr_info( "done.\n"); + dev_info(vortex->card->dev, "init.... done.\n"); spin_lock_init(&vortex->lock); return 0; @@ -2738,7 +2744,7 @@ static int vortex_core_init(vortex_t *vortex) static int vortex_core_shutdown(vortex_t * vortex) { - pr_info( "Vortex: shutdown..."); + dev_info(vortex->card->dev, "shutdown started\n"); #ifndef CHIP_AU8820 vortex_eq_free(vortex); vortex_Vort3D_disable(vortex); @@ -2760,7 +2766,7 @@ static int vortex_core_shutdown(vortex_t * vortex) msleep(5); hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff); - pr_info( "done.\n"); + dev_info(vortex->card->dev, "shutdown.... done.\n"); return 0; } @@ -2794,7 +2800,8 @@ static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v) break; default: fmt = 0x8; - pr_err( "vortex: format unsupported %d\n", alsafmt); + dev_err(v->card->dev, + "format unsupported %d\n", alsafmt); break; } return fmt; diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c index 9404ba7..9585c5c 100644 --- a/sound/pci/au88x0/au88x0_eq.c +++ b/sound/pci/au88x0/au88x0_eq.c @@ -845,7 +845,8 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count); if (count != 20) { - pr_err( "vortex: peak count error 20 != %d \n", count); + dev_err(vortex->card->dev, + "peak count error 20 != %d\n", count); return -1; } for (i = 0; i < 20; i++) diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index 72daf6c..151815b 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -98,7 +98,8 @@ static int vortex_gameport_register(vortex_t *vortex) vortex->gameport = gp = gameport_allocate_port(); if (!gp) { - pr_err( "vortex: cannot allocate memory for gameport\n"); + dev_err(vortex->card->dev, + "cannot allocate memory for gameport\n"); return -ENOMEM; } diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 328c194..1025e55 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -73,7 +73,7 @@ static int snd_vortex_midi(vortex_t *vortex) /* Check if anything is OK. */ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA); if (temp != MPU401_ACK /*0xfe */ ) { - pr_err( "midi port doesn't acknowledge!\n"); + dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n"); return -ENODEV; } /* Enable MPU401 interrupts. */ diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index bdde182..a6d6d8d 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -227,7 +227,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) { - pr_err( "Vortex: pcm page alloc failed!\n"); + dev_err(chip->card->dev, "Vortex: pcm page alloc failed!\n"); return err; } /* @@ -371,7 +371,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } #ifndef CHIP_AU8810 else { - pr_info( "vortex: wt start %d\n", dma); + dev_info(chip->card->dev, "wt start %d\n", dma); vortex_wtdma_startfifo(chip, dma); } #endif @@ -384,7 +384,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) vortex_adbdma_stopfifo(chip, dma); #ifndef CHIP_AU8810 else { - pr_info( "vortex: wt stop %d\n", dma); + dev_info(chip->card->dev, "wt stop %d\n", dma); vortex_wtdma_stopfifo(chip, dma); } #endif diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c index f094bac..78e12f4 100644 --- a/sound/pci/au88x0/au88x0_synth.c +++ b/sound/pci/au88x0/au88x0_synth.c @@ -90,7 +90,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_PARM(wt, 2), 0); temp = hwread(vortex->mmio, WT_PARM(wt, 3)); - pr_debug( "vortex: WT PARM3: %x\n", temp); + dev_dbg(vortex->card->dev, "WT PARM3: %x\n", temp); //hwwrite(vortex->mmio, WT_PARM(wt, 3), temp); hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0); @@ -98,7 +98,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0); hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0); - pr_debug( "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + dev_dbg(vortex->card->dev, "WT GMODE: %x\n", + hwread(vortex->mmio, WT_GMODE(wt))); hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff); hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810); @@ -106,7 +107,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) voice->parm0 = voice->parm1 = 0xcfb23e2f; hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0); hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1); - pr_debug( "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + dev_dbg(vortex->card->dev, "WT GMODE 2 : %x\n", + hwread(vortex->mmio, WT_GMODE(wt))); return 0; } @@ -196,14 +198,15 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) { if (wt >= (NR_WT / NR_WT_PB)) { - pr_warn - ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n", - reg, wt); + dev_warn(vortex->card->dev, + "WT SetReg: bank out of range. reg=0x%x, wt=%d\n", + reg, wt); return 0; } } else { if (wt >= NR_WT) { - pr_err( "vortex: WT SetReg: voice out of range\n"); + dev_err(vortex->card->dev, + "WT SetReg: voice out of range\n"); return 0; } } -- cgit v0.10.2 From 54841a06c54eb55918948c12ab9b5f02cacb6ab3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 15 Oct 2014 14:00:16 +0200 Subject: ALSA: seq: Use atomic ops for autoload refcount ... just to robustify for races. Signed-off-by: Takashi Iwai diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 91a786a..775ea93 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -127,15 +127,15 @@ static void snd_seq_device_info(struct snd_info_entry *entry, #ifdef CONFIG_MODULES /* avoid auto-loading during module_init() */ -static int snd_seq_in_init; +static atomic_t snd_seq_in_init = ATOMIC_INIT(0); void snd_seq_autoload_lock(void) { - snd_seq_in_init++; + atomic_inc(&snd_seq_in_init); } void snd_seq_autoload_unlock(void) { - snd_seq_in_init--; + atomic_dec(&snd_seq_in_init); } #endif @@ -147,7 +147,7 @@ void snd_seq_device_load_drivers(void) /* Calling request_module during module_init() * may cause blocking. */ - if (snd_seq_in_init) + if (atomic_read(&snd_seq_in_init)) return; mutex_lock(&ops_mutex); -- cgit v0.10.2 From 68ab61084de3220e2fb0a698c890ba91decddc85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 15 Oct 2014 14:06:25 +0200 Subject: ALSA: seq: bind seq driver automatically Currently the sequencer module binding is performed independently from the card module itself. The reason behind it is to keep the sequencer stuff optional and allow the system running without it (e.g. for using PCM or rawmidi only). This works in most cases, but a remaining problem is that the binding isn't done automatically when a new driver module is probed. Typically this becomes visible when a hotplug driver like usb audio is used. This patch tries to address this and other potential issues. First, the seq-binder (seq_device.c) tries to load a missing driver module at creating a new device object. This is done asynchronously in a workq for avoiding the deadlock (modprobe call in module init path). This action, however, should be enabled only when the sequencer stuff was already initialized, i.e. snd-seq module was already loaded. For that, a new function, snd_seq_autoload_init() is introduced here; this clears the blocking of autoloading, and also tries to load all pending driver modules. Reported-by: Adam Goode Signed-off-by: Takashi Iwai diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index 2398521..eea5400 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -108,9 +108,13 @@ int snd_seq_event_port_detach(int client, int port); #ifdef CONFIG_MODULES void snd_seq_autoload_lock(void); void snd_seq_autoload_unlock(void); +void snd_seq_autoload_init(void); +#define snd_seq_autoload_exit() snd_seq_autoload_lock() #else #define snd_seq_autoload_lock() #define snd_seq_autoload_unlock() +#define snd_seq_autoload_init() +#define snd_seq_autoload_exit() #endif #endif /* __SOUND_SEQ_KERNEL_H */ diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 7121105..bebdd2e 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -110,6 +110,7 @@ static int __init alsa_seq_init(void) if ((err = snd_seq_system_client_init()) < 0) goto error; + snd_seq_autoload_init(); error: snd_seq_autoload_unlock(); return err; @@ -131,6 +132,8 @@ static void __exit alsa_seq_exit(void) /* release event memory */ snd_sequencer_memory_done(); + + snd_seq_autoload_exit(); } module_init(alsa_seq_init) diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 775ea93..a8e2c60 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -56,6 +56,7 @@ MODULE_LICENSE("GPL"); #define DRIVER_LOADED (1<<0) #define DRIVER_REQUESTED (1<<1) #define DRIVER_LOCKED (1<<2) +#define DRIVER_REQUESTING (1<<3) struct ops_list { char id[ID_LEN]; /* driver id */ @@ -127,7 +128,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry, #ifdef CONFIG_MODULES /* avoid auto-loading during module_init() */ -static atomic_t snd_seq_in_init = ATOMIC_INIT(0); +static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ void snd_seq_autoload_lock(void) { atomic_inc(&snd_seq_in_init); @@ -137,32 +138,72 @@ void snd_seq_autoload_unlock(void) { atomic_dec(&snd_seq_in_init); } -#endif -void snd_seq_device_load_drivers(void) +static void autoload_drivers(void) { -#ifdef CONFIG_MODULES - struct ops_list *ops; + /* avoid reentrance */ + if (atomic_inc_return(&snd_seq_in_init) == 1) { + struct ops_list *ops; + + mutex_lock(&ops_mutex); + list_for_each_entry(ops, &opslist, list) { + if ((ops->driver & DRIVER_REQUESTING) && + !(ops->driver & DRIVER_REQUESTED)) { + ops->used++; + mutex_unlock(&ops_mutex); + ops->driver |= DRIVER_REQUESTED; + request_module("snd-%s", ops->id); + mutex_lock(&ops_mutex); + ops->used--; + } + } + mutex_unlock(&ops_mutex); + } + atomic_dec(&snd_seq_in_init); +} - /* Calling request_module during module_init() - * may cause blocking. - */ - if (atomic_read(&snd_seq_in_init)) - return; +static void call_autoload(struct work_struct *work) +{ + autoload_drivers(); +} - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (! (ops->driver & DRIVER_LOADED) && - ! (ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } +static DECLARE_WORK(autoload_work, call_autoload); + +static void try_autoload(struct ops_list *ops) +{ + if (!ops->driver) { + ops->driver |= DRIVER_REQUESTING; + schedule_work(&autoload_work); } +} + +static void queue_autoload_drivers(void) +{ + struct ops_list *ops; + + mutex_lock(&ops_mutex); + list_for_each_entry(ops, &opslist, list) + try_autoload(ops); mutex_unlock(&ops_mutex); +} + +void snd_seq_autoload_init(void) +{ + atomic_dec(&snd_seq_in_init); +#ifdef CONFIG_SND_SEQUENCER_MODULE + /* initial autoload only when snd-seq is a module */ + queue_autoload_drivers(); +#endif +} +#else +#define try_autoload(ops) /* NOP */ +#endif + +void snd_seq_device_load_drivers(void) +{ +#ifdef CONFIG_MODULES + queue_autoload_drivers(); + flush_work(&autoload_work); #endif } @@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, ops->num_devices++; mutex_unlock(&ops->reg_mutex); - unlock_driver(ops); - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { snd_seq_device_free(dev); return err; } + try_autoload(ops); + unlock_driver(ops); + if (result) *result = dev; @@ -554,6 +596,9 @@ static int __init alsa_seq_device_init(void) static void __exit alsa_seq_device_exit(void) { +#ifdef CONFIG_MODULES + cancel_work_sync(&autoload_work); +#endif remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); @@ -570,6 +615,7 @@ EXPORT_SYMBOL(snd_seq_device_new); EXPORT_SYMBOL(snd_seq_device_register_driver); EXPORT_SYMBOL(snd_seq_device_unregister_driver); #ifdef CONFIG_MODULES +EXPORT_SYMBOL(snd_seq_autoload_init); EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); #endif -- cgit v0.10.2 From d5129f33a0d155d69cb0652cfc87bbc4d132ca17 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 15 Oct 2014 14:09:42 +0200 Subject: Subject: ALSA: seq: Remove autoload locks in driver registration Since we're calling request_module() asynchronously now, we can get rid of the autoload lock in snd_seq_device_register_driver(), as well as in the snd-seq driver registration itself. This enables the automatic loading of dependent sequencer modules, such as snd-seq-virmidi from snd-emu10k1-synth. Signed-off-by: Takashi Iwai diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index bebdd2e..7e0aabb 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -86,7 +86,6 @@ static int __init alsa_seq_init(void) { int err; - snd_seq_autoload_lock(); if ((err = client_init_data()) < 0) goto error; @@ -112,7 +111,6 @@ static int __init alsa_seq_init(void) snd_seq_autoload_init(); error: - snd_seq_autoload_unlock(); return err; } diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index a8e2c60..0631bda 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -360,16 +360,12 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, entry->init_device == NULL || entry->free_device == NULL) return -EINVAL; - snd_seq_autoload_lock(); ops = find_driver(id, 1); - if (ops == NULL) { - snd_seq_autoload_unlock(); + if (ops == NULL) return -ENOMEM; - } if (ops->driver & DRIVER_LOADED) { pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); unlock_driver(ops); - snd_seq_autoload_unlock(); return -EBUSY; } @@ -386,7 +382,6 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, mutex_unlock(&ops->reg_mutex); unlock_driver(ops); - snd_seq_autoload_unlock(); return 0; } -- cgit v0.10.2 From 49fd46d2ffc92de3f4bd2e337f60b8b6052dc8b6 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 19 Oct 2014 09:11:25 +0200 Subject: ALSA: snd-usb: drop unused varible assigments Don't assign 'len' in cases where we don't make use of the returned value. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 2e4a9db..63a8adb 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1290,9 +1290,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = snprintf(kctl->id.name, - sizeof(kctl->id.name), - "Feature %d", unitid); + snprintf(kctl->id.name, sizeof(kctl->id.name), + "Feature %d", unitid); } if (!mapped_name) @@ -1305,9 +1304,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, */ if (!mapped_name && !(state->oterm.type >> 16)) { if ((state->oterm.type & 0xff00) == 0x0100) - len = append_ctl_name(kctl, " Capture"); + append_ctl_name(kctl, " Capture"); else - len = append_ctl_name(kctl, " Playback"); + append_ctl_name(kctl, " Playback"); } append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); -- cgit v0.10.2 From 03ad6a8c93b6df2d65c305b5b5f9474068b45bfb Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 16 Oct 2014 15:33:45 +0200 Subject: ALSA: hda - Fix "PCM" name being used on one DAC when there are two DACs In the scenario where there is one "Line Out", one "Speaker" and one "Headphone", and there are only two DACs, two outputs will share a DAC. Currently any mixer on such a DAC will get the "PCM" name, which is misleading. Instead use "Headphone+LO" or "Speaker+LO" to better specify what the volume actually controls. [fixed missing slave string additions by tiwai] Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 64220c0..dc13cce 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1038,6 +1038,19 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, break; *index = ch; return "Headphone"; + case AUTO_PIN_LINE_OUT: + /* This deals with the case where we have two DACs and + * one LO, one HP and one Speaker */ + if (!ch && cfg->speaker_outs && cfg->hp_outs) { + bool hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); + bool spk_lo_shared = !path_has_mixer(codec, spec->speaker_paths[0], ctl_type); + if (hp_lo_shared && spk_lo_shared) + return spec->vmaster_mute.hook ? "PCM" : "Master"; + if (hp_lo_shared) + return "Headphone+LO"; + if (spk_lo_shared) + return "Speaker+LO"; + } } /* for a single channel output, we don't have to name the channel */ @@ -4524,7 +4537,7 @@ static const char * const slave_pfxs[] = { "CLFE", "Bass Speaker", "PCM", "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", "Headphone Front", "Headphone Surround", "Headphone CLFE", - "Headphone Side", + "Headphone Side", "Headphone+LO", "Speaker+LO", NULL, }; -- cgit v0.10.2 From 3abb4f4d0e7aaad0d12004b5057f4486a688752b Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 16 Oct 2014 15:33:46 +0200 Subject: ALSA: hda - Use "Line Out" name instead of "PCM" when there are other outputs In case there are speakers or headphones as well, anything that only covers the line out should not be labelled "PCM". Let's name it "Line Out" instead for clarity. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index dc13cce..06d7210 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1055,7 +1055,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, /* for a single channel output, we don't have to name the channel */ if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; + return "Line Out"; if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); -- cgit v0.10.2 From 8e648204194cb51df8562d1e2a64b7dc6b0aead3 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 20 Oct 2014 14:38:09 +0200 Subject: ALSA: Update control names documentation This document was not really up-to-date. Add recent additions to this standard - based on what the HDA driver currently does, which is some kind of a de facto standard. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/ControlNames.txt b/Documentation/sound/alsa/ControlNames.txt index fea65bb..79a6127 100644 --- a/Documentation/sound/alsa/ControlNames.txt +++ b/Documentation/sound/alsa/ControlNames.txt @@ -1,6 +1,6 @@ This document describes standard names of mixer controls. -Syntax: SOURCE [DIRECTION] FUNCTION +Syntax: [LOCATION] SOURCE [CHANNEL] [DIRECTION] FUNCTION DIRECTION: (both directions) @@ -14,12 +14,29 @@ FUNCTION: Volume Route (route control, hardware specific) +CHANNEL: + (channel independent, or applies to all channels) + Front + Surround (rear left/right in 4.0/5.1 surround) + CLFE + Center + LFE + Side (side left/right for 7.1 surround) + +LOCATION: (physical location of source) + Front + Rear + Dock (docking station) + Internal + SOURCE: Master Master Mono Hardware Master Speaker (internal speaker) + Bass Speaker (internal LFE speaker) Headphone + Line Out Beep (beep generator) Phone Phone Input @@ -27,14 +44,14 @@ SOURCE: Synth FM Mic - Line + Headset Mic (mic part of combined headset jack - 4-pin headphone + mic) + Headphone Mic (mic part of either/or - 3-pin headphone or mic) + Line (input only, use "Line Out" for output) CD Video Zoom Video Aux PCM - PCM Front - PCM Rear PCM Pan Loopback Analog Loopback (D/A -> A/D loopback) @@ -47,8 +64,13 @@ SOURCE: Music I2S IEC958 + HDMI + SPDIF (output only) + SPDIF In + Digital In + HDMI/DP (either HDMI or DisplayPort) -Exceptions: +Exceptions (deprecated): [Digital] Capture Source [Digital] Capture Switch (aka input gain switch) [Digital] Capture Volume (aka input gain volume) -- cgit v0.10.2 From be93709cb13a1947fec3493267d04cd87baf497e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:06:04 +0200 Subject: ALSA: doc: Recommend the use of snd_ctl_elem_info() Instead of the open code for the info call back of enum elements, recommend the use of snd_ctl_elem_info(), which will reduce lots of codes. Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index 784793d..84ef6a9 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -3658,6 +3658,29 @@ struct _snd_pcm_runtime { + The above callback can be simplified with a helper function, + snd_ctl_enum_info. The final code + looks like below. + (You can pass ARRAY_SIZE(texts) instead of 4 in the third + argument; it's a matter of taste.) + + + + + + + + + Some common info callbacks are available for your convenience: snd_ctl_boolean_mono_info() and snd_ctl_boolean_stereo_info(). -- cgit v0.10.2 From df803e1389716bcdf11932fff47d7f1fc198bc8a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:07:21 +0200 Subject: ALSA: control: Warn if too long string is passed to snd_ctl_enum_info() This allows us to catch the bugs in drivers easily. Signed-off-by: Takashi Iwai diff --git a/sound/core/control.c b/sound/core/control.c index b961134..f95df84 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1747,6 +1747,9 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels, info->value.enumerated.items = items; if (info->value.enumerated.item >= items) info->value.enumerated.item = items - 1; + WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name), + "ALSA: too long item name '%s'\n", + names[info->value.enumerated.item]); strlcpy(info->value.enumerated.name, names[info->value.enumerated.item], sizeof(info->value.enumerated.name)); -- cgit v0.10.2 From a7e6fb99150ebb2852ebd0e7bad9ce37cc9a79dd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:08:50 +0200 Subject: ALSA: control: Allow to pass items zero to snd_ctl_enum_info() Although this is weird, some drivers want to allow empty control elements intentionally, e.g. the number of items may change depending on the firmware status. Let the function simply returning in such a case. Signed-off-by: Takashi Iwai diff --git a/sound/core/control.c b/sound/core/control.c index f95df84..5c35bba 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1745,6 +1745,8 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels, info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; info->count = channels; info->value.enumerated.items = items; + if (!items) + return 0; if (info->value.enumerated.item >= items) info->value.enumerated.item = items - 1; WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name), -- cgit v0.10.2 From 04eeb606a8383b306f4bc6991da8231b5f3924b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:10:39 +0200 Subject: ALSA: aoa: Use snd_ctl_elem_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 401107b..23c371ec 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -243,13 +243,7 @@ static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, { static const char * const texts[] = { "Line-In", "Microphone" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index cf3c630..364c7c4 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -478,15 +478,9 @@ static struct snd_kcontrol_new drc_switch_control = { static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Line-In", "Microphone" }; + static const char * const texts[] = { "Line-In", "Microphone" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 6d416f594bf9a290406d267e2627c5286f51ea59 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:11:28 +0200 Subject: ALSA: mts64: Use snd_ctl_elem_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index f5fd448..0388fbb 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -604,21 +604,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_frames = { static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { "24", - "25", - "29.97", - "30D", - "30" }; + static const char * const texts[5] = { + "24", "25", "29.97", "30D", "30" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl, -- cgit v0.10.2 From 7f471fd40742a5d87d887375430bf40331cbbcf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:11:58 +0200 Subject: ALSA: vx: Use snd_ctl_elem_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 3b6823f..be9477e 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -471,30 +471,18 @@ static struct snd_kcontrol_new vx_control_output_level = { */ static int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts_mic[3] = { + static const char * const texts_mic[3] = { "Digital", "Line", "Mic" }; - static char *texts_vx2[2] = { + static const char * const texts_vx2[2] = { "Digital", "Analog" }; struct vx_core *chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (chip->type >= VX_TYPE_VXPOCKET) { - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts_mic[uinfo->value.enumerated.item]); - } else { - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts_vx2[uinfo->value.enumerated.item]); - } - return 0; + if (chip->type >= VX_TYPE_VXPOCKET) + return snd_ctl_enum_info(uinfo, 1, 3, texts_mic); + else + return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2); } static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -539,18 +527,11 @@ static struct snd_kcontrol_new vx_control_audio_src = { */ static int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Auto", "Internal", "External" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From e7ced4137d859c576130ce7605e5fdd13221793d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Oct 2014 08:26:10 +0200 Subject: ALSA: bebob: More constify text arrays Signed-off-by: Takashi Iwai diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index e13eef9..dfbcd23 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -52,7 +52,7 @@ extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES]; #define SND_BEBOB_CLOCK_INTERNAL "Internal" struct snd_bebob_clock_spec { unsigned int num; - char *const *labels; + const char *const *labels; int (*get)(struct snd_bebob *bebob, unsigned int *id); }; struct snd_bebob_rate_spec { @@ -61,7 +61,7 @@ struct snd_bebob_rate_spec { }; struct snd_bebob_meter_spec { unsigned int num; - char *const *labels; + const char *const *labels; int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size); }; struct snd_bebob_spec { diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c index 45a0eed..a45a869 100644 --- a/sound/firewire/bebob/bebob_focusrite.c +++ b/sound/firewire/bebob/bebob_focusrite.c @@ -101,11 +101,11 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value) &data, sizeof(__be32), 0); } -static char *const saffirepro_26_clk_src_labels[] = { +static const char *const saffirepro_26_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock" }; -static char *const saffirepro_10_clk_src_labels[] = { +static const char *const saffirepro_10_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock" }; static int @@ -161,7 +161,7 @@ end: } struct snd_bebob_spec saffire_le_spec; -static char *const saffire_both_clk_src_labels[] = { +static const char *const saffire_both_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF" }; static int @@ -176,12 +176,12 @@ saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id) return err; }; -static char *const saffire_le_meter_labels[] = { +static const char *const saffire_le_meter_labels[] = { ANA_IN, ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, STM_IN, STM_IN }; -static char *const saffire_meter_labels[] = { +static const char *const saffire_meter_labels[] = { ANA_IN, ANA_IN, STM_IN, STM_IN, STM_IN, STM_IN, STM_IN, }; diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 70faa3a..34cba80 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -340,7 +340,7 @@ end: } /* Clock source control for special firmware */ -static char *const special_clk_labels[] = { +static const char *const special_clk_labels[] = { SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital", "Word Clock", SND_BEBOB_CLOCK_INTERNAL}; static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) @@ -438,7 +438,7 @@ static struct snd_kcontrol_new special_sync_ctl = { }; /* Digital input interface control for special firmware */ -static char *const special_dig_in_iface_labels[] = { +static const char *const special_dig_in_iface_labels[] = { "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical" }; static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl, @@ -539,7 +539,7 @@ static struct snd_kcontrol_new special_dig_in_iface_ctl = { }; /* Digital output interface control for special firmware */ -static char *const special_dig_out_iface_labels[] = { +static const char *const special_dig_out_iface_labels[] = { "S/PDIF Optical and Coaxial", "ADAT Optical" }; static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl, @@ -631,7 +631,7 @@ end: } /* Hardware metering for special firmware */ -static char *const special_meter_labels[] = { +static const char *const special_meter_labels[] = { ANA_IN, ANA_IN, ANA_IN, ANA_IN, SPDIF_IN, ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN, @@ -671,30 +671,30 @@ end: } /* last 4 bytes are omitted because it's clock info. */ -static char *const fw410_meter_labels[] = { +static const char *const fw410_meter_labels[] = { ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT, HP_OUT }; -static char *const audiophile_meter_labels[] = { +static const char *const audiophile_meter_labels[] = { ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, DIG_OUT, HP_OUT, AUX_OUT, }; -static char *const solo_meter_labels[] = { +static const char *const solo_meter_labels[] = { ANA_IN, DIG_IN, STRM_IN, STRM_IN, ANA_OUT, DIG_OUT }; /* no clock info */ -static char *const ozonic_meter_labels[] = { +static const char *const ozonic_meter_labels[] = { ANA_IN, ANA_IN, STRM_IN, STRM_IN, ANA_OUT, ANA_OUT }; /* TODO: need testers. these positions are based on authour's assumption */ -static char *const nrv10_meter_labels[] = { +static const char *const nrv10_meter_labels[] = { ANA_IN, ANA_IN, ANA_IN, ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c index 0e4c0bf..83b6772 100644 --- a/sound/firewire/bebob/bebob_terratec.c +++ b/sound/firewire/bebob/bebob_terratec.c @@ -8,7 +8,7 @@ #include "./bebob.h" -static char *const phase88_rack_clk_src_labels[] = { +static const char *const phase88_rack_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock" }; static int @@ -29,7 +29,7 @@ end: return err; } -static char *const phase24_series_clk_src_labels[] = { +static const char *const phase24_series_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "Digital In" }; static int diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c index 9b7e798..ef1fe38 100644 --- a/sound/firewire/bebob/bebob_yamaha.c +++ b/sound/firewire/bebob/bebob_yamaha.c @@ -28,7 +28,7 @@ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless. */ -static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; +static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; static int clk_src_get(struct snd_bebob *bebob, unsigned int *id) { -- cgit v0.10.2 From 41be5164ea09c92d551e8007d2543418e40f847a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:12:26 +0200 Subject: ALSA: bebob: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 34cba80..a422aaa 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -352,17 +352,8 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) static int special_clk_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_clk_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels), + special_clk_labels); } static int special_clk_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) @@ -444,17 +435,9 @@ static const char *const special_dig_in_iface_labels[] = { static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_dig_in_iface_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_dig_in_iface_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, + ARRAY_SIZE(special_dig_in_iface_labels), + special_dig_in_iface_labels); } static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) @@ -545,17 +528,9 @@ static const char *const special_dig_out_iface_labels[] = { static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_dig_out_iface_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_dig_out_iface_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, + ARRAY_SIZE(special_dig_out_iface_labels), + special_dig_out_iface_labels); } static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) -- cgit v0.10.2 From 609e478b40aceaa07d14f1bada02a3874bac2c45 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:13:01 +0200 Subject: ALSA: ak4xxx-adda: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index f3735e6..67dbfde 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -465,17 +465,10 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "44.1kHz", "Off", "48kHz", "32kHz", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol, @@ -570,22 +563,13 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, { struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); int mixer_ch = AK_GET_SHIFT(kcontrol->private_value); - const char **input_names; - unsigned int num_names, idx; + unsigned int num_names; num_names = ak4xxx_capture_num_inputs(ak, mixer_ch); if (!num_names) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_names; - idx = uinfo->value.enumerated.item; - if (idx >= num_names) - return -EINVAL; - input_names = ak->adc_info[mixer_ch].input_names; - strlcpy(uinfo->value.enumerated.name, input_names[idx], - sizeof(uinfo->value.enumerated.name)); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_names, + ak->adc_info[mixer_ch].input_names); } static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 1da0c47779840d038583ea6fa4e3497939d6ea21 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:13:39 +0200 Subject: ALSA: ad1816a: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index f0fd98e..01a0798 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -731,18 +731,12 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device, static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "Line", "Mix", "CD", "Synth", "Video", "Mic", "Phone", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item > 6) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 7, texts); } static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 69b0c762cfd4c5e86a5b1fc0074889881b859c4a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:14:07 +0200 Subject: ALSA: es1688: Use snd_ctl_enum_info() ... and reduce the open codes. Also correct the array size and add missing const. Signed-off-by: Takashi Iwai diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index b3b4f15..de810e4 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -762,18 +762,12 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[9] = { + static const char * const texts[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 4e28350a8c54a8b873b610311eb384a619c4045b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:14:24 +0200 Subject: ALSA: es18xx: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 6faaac6..63e7323 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -964,44 +964,28 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream) static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts5Source[5] = { + static const char * const texts5Source[5] = { "Mic", "CD", "Line", "Master", "Mix" }; - static char *texts8Source[8] = { + static const char * const texts8Source[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (chip->version) { case 0x1868: case 0x1878: - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts5Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 4, texts5Source); case 0x1887: case 0x1888: - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts5Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 5, texts5Source); case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */ case 0x1879: - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts8Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 8, texts8Source); default: return -EINVAL; } - return 0; } static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 8edd2d120b6821d6eba9fedd0b17b7a47fbe9181 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:14:41 +0200 Subject: ALSA: msnd: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index 031dc69..17e49a0 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -55,20 +55,13 @@ static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Analog", "MASS", "SPDIF", }; struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = items; - if (uinfo->value.enumerated.item >= items) - uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, items, texts); } static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 6b9e1288a506b4d10c48a67058532c52c5c46240 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:14:51 +0200 Subject: ALSA: sb16: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 0bbcd47..72b10f4 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -702,17 +702,11 @@ static int snd_sb16_get_dma_mode(struct snd_sb *chip) static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Auto", "Playback", "Capture" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 1ff78ec..e403334 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -182,17 +182,11 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[5] = { + static const char * const texts[5] = { "CD", "Mic", "Line", "Synth", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -275,18 +269,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "L chan only", "R chan only", "L ch/2 + R ch/2" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol, @@ -335,17 +322,11 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol, static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "Mic", "CD", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } -- cgit v0.10.2 From 0773efa53260a57d6e3bc38469c216635700537e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:15:06 +0200 Subject: ALSA: wss: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 360b08b..347bb1b 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -1993,25 +1993,20 @@ EXPORT_SYMBOL(snd_wss_timer); static int snd_wss_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "Line", "Aux", "Mic", "Mix" }; - static char *opl3sa_texts[4] = { + static const char * const opl3sa_texts[4] = { "Line", "CD", "Mic", "Mix" }; - static char *gusmax_texts[4] = { + static const char * const gusmax_texts[4] = { "Line", "Synth", "Mic", "Mix" }; - char **ptexts = texts; + const char * const *ptexts = texts; struct snd_wss *chip = snd_kcontrol_chip(kcontrol); if (snd_BUG_ON(!chip->card)) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; if (!strcmp(chip->card->driver, "GUS MAX")) ptexts = gusmax_texts; switch (chip->hardware) { @@ -2023,8 +2018,7 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol, ptexts = opl3sa_texts; break; } - strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 4, ptexts); } static int snd_wss_get_mux(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 2a2085ab198439c8f08be2f6b9b5cbc9e93877b7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:15:16 +0200 Subject: ALSA: mips: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index 04bb06c..33b08fc 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -201,17 +201,10 @@ static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol, static int sgio2audio_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "Cam Mic", "Mic", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= 3) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int sgio2audio_source_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 3c6a73cc6b6ccd9188b3405c744365c0874b9274 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:15:26 +0200 Subject: ALSA: parisc: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 4b20be7..29604a2 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -776,15 +776,9 @@ static int snd_harmony_captureroute_info(struct snd_kcontrol *kc, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[2] = { "Line", "Mic" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int -- cgit v0.10.2 From 3b7a00dc9e4277d6fcad68dd1db35f77264ede5f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:15:36 +0200 Subject: ALSA: ac97: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 14ad54b..5ee2f17 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -463,14 +463,8 @@ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, { struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->mask; - - if (uinfo->value.enumerated.item > e->mask - 1) - uinfo->value.enumerated.item = e->mask - 1; - strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, + e->mask, e->texts); } static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 9917622..50f420d 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -81,22 +81,11 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi /* * shared line-in/mic controls */ -static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo, - const char **texts, unsigned int nums) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = nums; - if (uinfo->value.enumerated.item > nums - 1) - uinfo->value.enumerated.item = nums - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "Shared", "Independent" }; - return ac97_enum_text_info(kcontrol, uinfo, texts, 2); + static const char * const texts[] = { "Shared", "Independent" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -123,9 +112,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" }; - return ac97_enum_text_info(kcontrol, uinfo, texts, - kcontrol->private_value); + static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" }; + + return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts); } static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -240,17 +229,11 @@ static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97) static int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Standard", "Small", "Smaller" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol, @@ -293,15 +276,9 @@ static const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker = static int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "AC-Link", "A/D Converter" }; + static const char * const texts[2] = { "AC-Link", "A/D Converter" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol, @@ -401,15 +378,9 @@ static int patch_yamaha_ymf743(struct snd_ac97 *ac97) There is also a bit to mute S/PDIF output in a vendor-specific register. */ static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" }; + static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1103,16 +1074,11 @@ static int patch_sigmatel_stac9756(struct snd_ac97 * ac97) static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { "Input/Disabled", "Front Output", + static const char * const texts[5] = { + "Input/Disabled", "Front Output", "Rear Output", "Center/LFE Output", "Mixer Output" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1147,16 +1113,11 @@ static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, stru static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack", + static const char * const texts[7] = { + "Mic2 Jack", "Mic1 Jack", "Line In Jack", "Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item > 6) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 7, texts); } static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1181,15 +1142,11 @@ static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struc static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "None", "Front Jack", "Rear Jack" }; + static const char * const texts[3] = { + "None", "Front Jack", "Rear Jack" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1804,15 +1761,9 @@ static int patch_ad1886(struct snd_ac97 * ac97) static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "AC-Link", "A/D Converter" }; + static const char * const texts[2] = { "AC-Link", "A/D Converter" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1994,15 +1945,9 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"}; + static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2153,16 +2098,11 @@ static int patch_ad1980(struct snd_ac97 * ac97) static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"}; + static const char * const texts[4] = { + "High-Z", "3.7 V", "2.25 V", "0 V" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol, @@ -2756,20 +2696,18 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = { static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" }; - static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" }; + static const char * const texts_655[3] = { + "PCM", "Analog In", "IEC958 In" + }; + static const char * const texts_658[4] = { + "PCM", "Analog1 In", "Analog2 In", "IEC958 In" + }; struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - ac97->spec.dev_flags ? - texts_658[uinfo->value.enumerated.item] : - texts_655[uinfo->value.enumerated.item]); - return 0; + if (ac97->spec.dev_flags) + return snd_ctl_enum_info(uinfo, 1, 4, texts_658); + else + return snd_ctl_enum_info(uinfo, 1, 3, texts_655); } static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3055,15 +2993,9 @@ static int patch_cm9738(struct snd_ac97 * ac97) static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Analog", "Digital" }; + static const char * const texts[] = { "Analog", "Digital" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3235,15 +3167,9 @@ static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = { static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" }; + static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3552,11 +3478,12 @@ static int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol, * is SM51EN *AND* it's Bit14, not Bit15 so the table is very * counter-intuitive */ - static const char* texts[] = { "LineIn Mic1", "LineIn Mic1 Mic3", + static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3", "Surr LFE/C Mic3", "LineIn LFE/C Mic3", "LineIn Mic2", "LineIn Mic2 Mic1", "Surr LFE Mic1", "Surr LFE Mic1 Mic2"}; - return ac97_enum_text_info(kcontrol, uinfo, texts, 8); + + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol, @@ -3720,9 +3647,8 @@ static struct vt1618_uaj_item vt1618_uaj[3] = { static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - return ac97_enum_text_info(kcontrol, uinfo, - vt1618_uaj[kcontrol->private_value].items, - 4); + return snd_ctl_enum_info(uinfo, 1, 4, + vt1618_uaj[kcontrol->private_value].items); } /* All of the vt1618 Universal Audio Jack twiddlers are on @@ -3767,9 +3693,9 @@ static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol, static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *txt_aux[] = {"Aux In", "Back Surr Out"}; + static const char * const txt_aux[] = {"Aux In", "Back Surr Out"}; - return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2); + return snd_ctl_enum_info(uinfo, 1, 2, txt_aux); } static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 1bc10bb68d348078af0eb8b64292ec542dcd7634 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 21 Oct 2014 08:51:45 +0200 Subject: ALSA: ac97: Constify more text arrays Signed-off-by: Takashi Iwai diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 50f420d..ceaac1c 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -33,7 +33,8 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, const char *name); static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, - const unsigned int *tlv, const char **slaves); + const unsigned int *tlv, + const char * const *slaves); /* * Chip specific initialization @@ -3196,7 +3197,9 @@ static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0); } -static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" }; +static const char * const cm9761_dac_clock[] = { + "AC-Link", "SPDIF-In", "Both" +}; static const struct ac97_enum cm9761_dac_clock_enum = AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock); @@ -3310,7 +3313,9 @@ static int patch_cm9761(struct snd_ac97 *ac97) #define AC97_CM9780_MULTI_CHAN 0x66 #define AC97_CM9780_SPDIF 0x6c -static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" }; +static const char * const cm9780_ch_select[] = { + "Front", "Side", "Center/LFE", "Rear" +}; static const struct ac97_enum cm9780_ch_select_enum = AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select); static const struct snd_kcontrol_new cm9780_controls[] = { @@ -3356,7 +3361,7 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0), AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0), }; -static const char *slave_vols_vt1616[] = { +static const char * const slave_vols_vt1616[] = { "Front Playback Volume", "Surround Playback Volume", "Center Playback Volume", @@ -3364,7 +3369,7 @@ static const char *slave_vols_vt1616[] = { NULL }; -static const char *slave_sws_vt1616[] = { +static const char * const slave_sws_vt1616[] = { "Front Playback Switch", "Surround Playback Switch", "Center Playback Switch", @@ -3385,10 +3390,11 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, /* create a virtual master control and add slaves */ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, - const unsigned int *tlv, const char **slaves) + const unsigned int *tlv, + const char * const *slaves) { struct snd_kcontrol *kctl; - const char **s; + const char * const *s; int err; kctl = snd_ctl_make_virtual_master(name, tlv); @@ -3612,7 +3618,7 @@ static int patch_vt1617a(struct snd_ac97 * ac97) struct vt1618_uaj_item { unsigned short mask; unsigned short shift; - const char *items[4]; + const char * const items[4]; }; /* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */ diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 47bf8df..d1ce151 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -49,7 +49,7 @@ struct ac97_enum { unsigned char shift_l; unsigned char shift_r; unsigned short mask; - const char **texts; + const char * const *texts; }; #define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ -- cgit v0.10.2 From 30d0ae425ab1c9bb0003c3798de78fbf30ddebdc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:15:50 +0200 Subject: ALSA: asihpi: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 5017176..ac66b32 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1625,18 +1625,7 @@ static const char * const asihpi_aesebu_format_names[] = { static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - asihpi_aesebu_format_names[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names); } static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, @@ -1863,22 +1852,7 @@ static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, if (num_bands < 0) return num_bands; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_bands; - - if (num_bands > 0) { - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - asihpi_tuner_band_names[ - tuner_bands[uinfo->value.enumerated.item]]); - - } - return 0; + return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names); } static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, @@ -2253,7 +2227,7 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; u16 mode; int i; - u16 mode_map[6]; + const char *mapped_names[6]; int valid_modes = 0; /* HPI channel mode values can be from 1 to 6 @@ -2262,24 +2236,14 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) if (!hpi_channel_mode_query_mode( h_control, i, &mode)) { - mode_map[valid_modes] = mode; + mapped_names[valid_modes] = mode_names[mode]; valid_modes++; } if (!valid_modes) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = valid_modes; - - if (uinfo->value.enumerated.item >= valid_modes) - uinfo->value.enumerated.item = valid_modes - 1; - - strcpy(uinfo->value.enumerated.name, - mode_names[mode_map[uinfo->value.enumerated.item]]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names); } static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 4d765e48c5edb2090b82e97680b2d1ddf6d18c31 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:16:01 +0200 Subject: ALSA: aw2: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 3878cf5..e1cf019 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -725,19 +725,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip) static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { + static const char * const texts[2] = { "Analog", "Digital" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) { - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 9b311a0ad9ec0df9f010bcadd19193b1cee593f6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:16:13 +0200 Subject: ALSA: azt3328: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 5a69e26..fdbb9c0 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1034,11 +1034,6 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, const char * const *p = NULL; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1; - uinfo->value.enumerated.items = reg.enum_c; - if (uinfo->value.enumerated.item > reg.enum_c - 1U) - uinfo->value.enumerated.item = reg.enum_c - 1U; if (reg.reg == IDX_MIXER_ADVCTL2) { switch(reg.lchan_shift) { case 8: /* modem out sel */ @@ -1051,12 +1046,12 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, p = texts4; break; } - } else - if (reg.reg == IDX_MIXER_REC_SELECT) + } else if (reg.reg == IDX_MIXER_REC_SELECT) p = texts3; - strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, + (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1, + reg.enum_c, p); } static int -- cgit v0.10.2 From de95eae25a2744ba5f9bd3c862bb43a1b177ad58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:16:26 +0200 Subject: ALSA: ca0106: Use snd_ctl_enum_info() ... and reduce the open codes. Also correct the array size and add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 27de0de..68c0eb0 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -185,17 +185,11 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[6] = { + static const char * const texts[6] = { "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 6; - if (uinfo->value.enumerated.item > 5) - uinfo->value.enumerated.item = 5; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 6, texts); } static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, @@ -228,17 +222,11 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[6] = { + static const char * const texts[4] = { "Phone", "Mic", "Line in", "Aux" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, @@ -273,29 +261,17 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Side out", "Line in" }; + static const char * const texts[2] = { "Side out", "Line in" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Line in", "Mic in" }; + static const char * const texts[2] = { "Line in", "Mic in" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From c69a4f3046ee5a28ab09a1786a73d04bd6177445 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:16:38 +0200 Subject: ALSA: echoaudio: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 631aaa4..d82321f 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1416,21 +1416,14 @@ static struct snd_kcontrol_new snd_echo_vmixer = { static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[4] = { + static const char * const names[4] = { "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical", "S/PDIF Cdrom" }; struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = chip->num_digital_modes; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= chip->num_digital_modes) - uinfo->value.enumerated.item = chip->num_digital_modes - 1; - strcpy(uinfo->value.enumerated.name, names[ - chip->digital_mode_list[uinfo->value.enumerated.item]]); - return 0; + return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names); } static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol, @@ -1509,16 +1502,9 @@ static struct snd_kcontrol_new snd_echo_digital_mode_switch = { static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[2] = {"Consumer", "Professional"}; + static const char * const names[2] = {"Consumer", "Professional"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = 2; - uinfo->count = 1; - if (uinfo->value.enumerated.item) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - names[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, names); } static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol, @@ -1566,21 +1552,14 @@ static struct snd_kcontrol_new snd_echo_spdif_mode_switch = { static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[8] = { + static const char * const names[8] = { "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync", "ESync96", "MTC" }; struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = chip->num_clock_sources; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= chip->num_clock_sources) - uinfo->value.enumerated.item = chip->num_clock_sources - 1; - strcpy(uinfo->value.enumerated.name, names[ - chip->clock_source_list[uinfo->value.enumerated.item]]); - return 0; + return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names); } static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 1541c66d3bb78c8a388025b074c75658c790b72f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:16:48 +0200 Subject: ALSA: emu10k1: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index c5ae2a2..1de3302 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -83,7 +83,7 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, * Items labels in enum mixer controls assigning source data to * each destination */ -static char *emu1010_src_texts[] = { +static const char * const emu1010_src_texts[] = { "Silence", "Dock Mic A", "Dock Mic B", @@ -141,7 +141,7 @@ static char *emu1010_src_texts[] = { /* 1616(m) cardbus */ -static char *emu1616_src_texts[] = { +static const char * const emu1616_src_texts[] = { "Silence", "Dock Mic A", "Dock Mic B", @@ -393,23 +393,11 @@ static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - char **items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { - uinfo->value.enumerated.items = 49; - items = emu1616_src_texts; - } else { - uinfo->value.enumerated.items = 53; - items = emu1010_src_texts; - } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - items[uinfo->value.enumerated.item]); - return 0; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + return snd_ctl_enum_info(uinfo, 1, 49, emu1616_src_texts); + else + return snd_ctl_enum_info(uinfo, 1, 53, emu1010_src_texts); } static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, @@ -699,19 +687,11 @@ static struct snd_kcontrol_new snd_emu1010_dac_pads[] = { static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "44100", "48000", "SPDIF", "ADAT" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; - - + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol, @@ -830,21 +810,15 @@ static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { #if 0 - static char *texts[4] = { + static const char * const texts[4] = { "Unknown1", "Unknown2", "Mic", "Line" }; #endif - static char *texts[2] = { + static const char * const texts[2] = { "Mic", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol, @@ -997,15 +971,9 @@ static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] = { #if 0 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"44100", "48000", "96000"}; + static const char * const texts[] = {"44100", "48000", "96000"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index a4fe7f0..7ef3898 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -757,18 +757,12 @@ static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol, static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S", "CDIF", "FX", "AC97" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol, @@ -805,15 +799,9 @@ static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "0", "1", "2", "3", }; + static const char * const texts[4] = { "0", "1", "2", "3", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 6b6b295e8053dd5a005aaa089b5bed4b4a65c632 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:17:08 +0200 Subject: ALSA: es1938: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 6399624..0fc46eb 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1045,18 +1045,12 @@ static int snd_es1938_new_pcm(struct es1938 *chip, int device) static int snd_es1938_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_es1938_get_mux(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From ca776a28ae10bb06807f23e807f0f459dab78318 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:17:17 +0200 Subject: ALSA: fm801: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index c503830..d167aff 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -958,17 +958,11 @@ static int snd_fm801_put_double(struct snd_kcontrol *kcontrol, static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { + static const char * const texts[5] = { "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 3ff72219320f616489bf0d98ddac12899da4a9ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:17:28 +0200 Subject: ALSA: hda: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 15e0089..259fbea 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2927,16 +2927,8 @@ static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol, static const char * const texts[] = { "On", "Off", "Follow Master" }; - unsigned int index; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - index = uinfo->value.enumerated.item; - if (index >= 3) - index = 2; - strcpy(uinfo->value.enumerated.name, texts[index]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol, @@ -5195,14 +5187,7 @@ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, texts = texts_default; } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_items; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info); -- cgit v0.10.2 From c4fa251f6f3ed00d59d0d8ee63bf346e6dd6b664 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:18:33 +0200 Subject: ALSA: ice1712: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 817a1bc..5cb587c 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -580,13 +580,7 @@ static int snd_ice1712_ewx_io_sense_info(struct snd_kcontrol *kcontrol, struct s static const char * const texts[2] = { "+4dBu", "-10dBV", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ice1712_ewx_io_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -903,13 +897,7 @@ static int snd_ice1712_6fire_select_input_info(struct snd_kcontrol *kcontrol, st static const char * const texts[4] = { "Internal", "Front Input", "Rear Input", "Wave Table" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 206ed2c..48a0c33 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1839,13 +1839,7 @@ static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol, "96000", /* 12: 7 */ "IEC958 Input", /* 13: -- */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 14; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 14, texts); } static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol, @@ -1930,13 +1924,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon "96000", /* 12: 7 */ /* "IEC958 Input", 13: -- */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 13; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 13, texts); } static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcontrol, @@ -2057,15 +2045,8 @@ static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol, "IEC958 In L", "IEC958 In R", /* 9-10 */ "Digital Mixer", /* 11 - optional */ }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = - snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + int num_items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 597da2e4dfa04c8ee66b09fce931ab6825bc3e75 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:18:50 +0200 Subject: ALSA: ice1724: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 3b3cf4a..c9411df 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -205,13 +205,7 @@ static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol, static const char * const texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol, @@ -1106,20 +1100,10 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in }; struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) { - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]); - } else { - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - } - return 0; + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) + return snd_ctl_enum_info(uinfo, 2, 8, universe_texts); + else + return snd_ctl_enum_info(uinfo, 2, 5, texts); } static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1167,16 +1151,10 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ "CD", "Coax" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71) - strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]); + return snd_ctl_enum_info(uinfo, 1, 2, prodigy_texts); else - strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, aureon_texts); } static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1392,15 +1370,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_ { static const char * const texts[2] = { "128x", "64x" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 08cb08a..f633e3b 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2049,13 +2049,7 @@ static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol, "IEC958 In L", "IEC958 In R", /* 3-4 */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static inline int analog_route_shift(int idx) diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c index 63aa39f..7de25c4 100644 --- a/sound/pci/ice1712/maya44.c +++ b/sound/pci/ice1712/maya44.c @@ -359,15 +359,7 @@ static int maya_rec_src_info(struct snd_kcontrol *kcontrol, { static const char * const texts[] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int maya_rec_src_get(struct snd_kcontrol *kcontrol, @@ -411,15 +403,7 @@ static int maya_pb_route_info(struct snd_kcontrol *kcontrol, "Input 1", "Input 2", "Input 3", "Input 4" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int maya_pb_route_shift(int idx) diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 0011e04..e9ca89c 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -723,17 +723,7 @@ static int phase28_oversampling_info(struct snd_kcontrol *k, { static const char * const texts[2] = { "128x", "64x" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 5555eb4..5101f40 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -417,13 +417,7 @@ static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_inf "Optical", /* RXP1 */ "CD", /* RXP2 */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index f3b491a..1eb151aa 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -284,15 +284,7 @@ static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, { static const char * const texts[2] = { "Line In", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } @@ -563,13 +555,7 @@ static int ak4114_input_sw_info(struct snd_kcontrol *kcontrol, { static const char * const texts[2] = { "Toslink", "Coax" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c index 2261d1e..2697402 100644 --- a/sound/pci/ice1712/prodigy_hifi.c +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -537,7 +537,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char* texts[32] = { + static const char * const texts[32] = { "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2, WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3, WM_AIN1 "+" WM_AIN2 "+" WM_AIN3, @@ -560,14 +560,7 @@ static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5 }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 32; - if (uinfo->value.enumerated.item > 31) - uinfo->value.enumerated.item = 31; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 32, texts); } static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 2c2df4b..d4caf9d 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -46,7 +46,7 @@ struct qtet_kcontrol_private { unsigned int bit; void (*set_register)(struct snd_ice1712 *ice, unsigned int val); unsigned int (*get_register)(struct snd_ice1712 *ice); - unsigned char * const texts[2]; + const char * const texts[2]; }; enum { @@ -554,17 +554,7 @@ static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol, { static const char * const texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol, @@ -706,17 +696,8 @@ static int qtet_enum_info(struct snd_kcontrol *kcontrol, { struct qtet_kcontrol_private private = qtet_privates[kcontrol->private_value]; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(private.texts); - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - private.texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(private.texts), + private.texts); } static int qtet_sw_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c index ffd894b..1c5d5b2 100644 --- a/sound/pci/ice1712/se.c +++ b/sound/pci/ice1712/se.c @@ -452,14 +452,7 @@ static int se200pci_cont_enum_info(struct snd_kcontrol *kc, c = se200pci_get_enum_count(n); if (!c) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = c; - if (uinfo->value.enumerated.item >= c) - uinfo->value.enumerated.item = c - 1; - strcpy(uinfo->value.enumerated.name, - se200pci_cont[n].member[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, c, se200pci_cont[n].member); } static int se200pci_cont_volume_get(struct snd_kcontrol *kc, -- cgit v0.10.2 From f861237c80a07449abd351c04a6ba397418dc0ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:58 +0200 Subject: ALSA: korg1212: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 9fe549b..59d21c9 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -444,9 +444,9 @@ static char *stateName[] = { "Invalid" }; -static char *clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; +static const char * const clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; -static char *clockSourceName[] = { +static const char * const clockSourceName[] = { "ADAT at 44.1 kHz", "ADAT at 48 kHz", "S/PDIF at 44.1 kHz", @@ -455,7 +455,7 @@ static char *clockSourceName[] = { "local clock at 48 kHz" }; -static char *channelName[] = { +static const char * const channelName[] = { "ADAT-1", "ADAT-2", "ADAT-3", @@ -1844,14 +1844,9 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, static int snd_korg1212_control_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; - uinfo->value.enumerated.items = kAudioChannels; - if (uinfo->value.enumerated.item > kAudioChannels-1) { - uinfo->value.enumerated.item = kAudioChannels-1; - } - strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, + (kcontrol->private_value >= 8) ? 2 : 1, + kAudioChannels, channelName); } static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol, @@ -1961,14 +1956,7 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol, static int snd_korg1212_control_sync_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) { - uinfo->value.enumerated.item = 2; - } - strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, clockSourceTypeName); } static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 08455ace3cafd9b0b5c35db3d89c4388f6d3a6fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:00 +0200 Subject: ALSA: pcxhr: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index 95c9571..63136c4 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -660,14 +660,7 @@ static int pcxhr_audio_src_info(struct snd_kcontrol *kcontrol, if (chip->mgr->board_has_mic) i = 5; /* Mic and MicroMix available */ } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = i; - if (uinfo->value.enumerated.item > (i-1)) - uinfo->value.enumerated.item = i-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, i, texts); } static int pcxhr_audio_src_get(struct snd_kcontrol *kcontrol, @@ -756,14 +749,7 @@ static int pcxhr_clock_type_info(struct snd_kcontrol *kcontrol, texts = textsPCXHR; snd_BUG_ON(clock_items > (PCXHR_CLOCK_TYPE_MAX+1)); } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = clock_items; - if (uinfo->value.enumerated.item >= clock_items) - uinfo->value.enumerated.item = clock_items-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, clock_items, texts); } static int pcxhr_clock_type_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 11c6ef7c8d439ef2bc3c95e5a4dcea449ab1f90f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:12 +0200 Subject: ALSA: rme32: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 4afd3ca..6c60dcd 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1608,30 +1608,24 @@ snd_rme32_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct rme32 *rme32 = snd_kcontrol_chip(kcontrol); - static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" }; + static const char * const texts[4] = { + "Optical", "Coaxial", "Internal", "XLR" + }; + int num_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (rme32->pci->device) { case PCI_DEVICE_ID_RME_DIGI32: case PCI_DEVICE_ID_RME_DIGI32_8: - uinfo->value.enumerated.items = 3; + num_items = 3; break; case PCI_DEVICE_ID_RME_DIGI32_PRO: - uinfo->value.enumerated.items = 4; + num_items = 4; break; default: snd_BUG(); - break; - } - if (uinfo->value.enumerated.item > - uinfo->value.enumerated.items - 1) { - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + return -EINVAL; } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_rme32_get_inputtype_control(struct snd_kcontrol *kcontrol, @@ -1695,20 +1689,12 @@ static int snd_rme32_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "AutoSync", + static const char * const texts[4] = { "AutoSync", "Internal 32.0kHz", "Internal 44.1kHz", "Internal 48.0kHz" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme32_get_clockmode_control(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 9c30d46a0fb3b294faf1226025071d6e802a8c36 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:23 +0200 Subject: ALSA: rme96: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 5a395c8..2f1a851 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1884,39 +1884,38 @@ snd_rme96_put_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ele static int snd_rme96_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; + static const char * const _texts[5] = { + "Optical", "Coaxial", "Internal", "XLR", "Analog" + }; struct rme96 *rme96 = snd_kcontrol_chip(kcontrol); - char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; + const char *texts[5] = { + _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] + }; + int num_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: case PCI_DEVICE_ID_RME_DIGI96_8: - uinfo->value.enumerated.items = 3; + num_items = 3; break; case PCI_DEVICE_ID_RME_DIGI96_8_PRO: - uinfo->value.enumerated.items = 4; + num_items = 4; break; case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST: if (rme96->rev > 4) { /* PST */ - uinfo->value.enumerated.items = 4; + num_items = 4; texts[3] = _texts[4]; /* Analog instead of XLR */ } else { /* PAD */ - uinfo->value.enumerated.items = 5; + num_items = 5; } break; default: snd_BUG(); - break; - } - if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + return -EINVAL; } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2002,16 +2001,9 @@ snd_rme96_put_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el static int snd_rme96_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "AutoSync", "Internal", "Word" }; + static const char * const texts[3] = { "AutoSync", "Internal", "Word" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) { - uinfo->value.enumerated.item = 2; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme96_get_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2041,16 +2033,11 @@ snd_rme96_put_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el static int snd_rme96_info_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; + static const char * const texts[4] = { + "0 dB", "-6 dB", "-12 dB", "-18 dB" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme96_get_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2081,16 +2068,9 @@ snd_rme96_put_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int snd_rme96_info_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; + static const char * const texts[4] = { "1+2", "3+4", "5+6", "7+8" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme96_get_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 8d678da9f0afbb951778369510c09b99de608c24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:34 +0200 Subject: ALSA: hdsp: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 7646ba1..2eb8baf 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1680,16 +1680,13 @@ static int hdsp_set_spdif_input(struct hdsp *hdsp, int in) static int snd_hdsp_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"Optical", "Coaxial", "Internal", "AES"}; + static const char * const texts[4] = { + "Optical", "Coaxial", "Internal", "AES" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ((hdsp->io_type == H9632) ? 4 : 3); - if (uinfo->value.enumerated.item > ((hdsp->io_type == H9632) ? 3 : 2)) - uinfo->value.enumerated.item = ((hdsp->io_type == H9632) ? 3 : 2); - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 4 : 3, + texts); } static int snd_hdsp_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1786,16 +1783,14 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, static int snd_hdsp_info_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; + static const char * const texts[] = { + "32000", "44100", "48000", "64000", "88200", "96000", + "None", "128000", "176400", "192000" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1872,14 +1867,13 @@ static int snd_hdsp_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct static int snd_hdsp_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7 ; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[] = { + "32000", "44100", "48000", "64000", "88200", "96000", + "None", "128000", "176400", "192000" + }; + + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1940,15 +1934,9 @@ static int hdsp_system_clock_mode(struct hdsp *hdsp) static int snd_hdsp_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Master", "Slave" }; + static const char * const texts[] = {"Master", "Slave" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_hdsp_get_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2049,19 +2037,16 @@ static int hdsp_set_clock_source(struct hdsp *hdsp, int mode) static int snd_hdsp_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" }; + static const char * const texts[] = { + "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", + "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", + "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", + "Internal 192.0 KHz" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (hdsp->io_type == H9632) - uinfo->value.enumerated.items = 10; - else - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2165,15 +2150,9 @@ static int hdsp_set_da_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"}; + static const char * const texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2250,15 +2229,9 @@ static int hdsp_set_ad_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"}; + static const char * const texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2335,15 +2308,9 @@ static int hdsp_set_phone_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"0 dB", "-6 dB", "-12 dB"}; + static const char * const texts[] = {"0 dB", "-6 dB", "-12 dB"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2439,31 +2406,28 @@ static int hdsp_set_pref_sync_ref(struct hdsp *hdsp, int pref) static int snd_hdsp_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" }; + static const char * const texts[] = { + "Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + int num_items; switch (hdsp->io_type) { case Digiface: case H9652: - uinfo->value.enumerated.items = 6; + num_items = 6; break; case Multiface: - uinfo->value.enumerated.items = 4; + num_items = 4; break; case H9632: - uinfo->value.enumerated.items = 3; + num_items = 3; break; default: return -EINVAL; } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_hdsp_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2543,15 +2507,11 @@ static int hdsp_autosync_ref(struct hdsp *hdsp) static int snd_hdsp_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" }; + static const char * const texts[] = { + "Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 7, texts); } static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2738,14 +2698,9 @@ static int snd_hdsp_put_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int snd_hdsp_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No Lock", "Lock", "Sync" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[] = {"No Lock", "Lock", "Sync" }; + + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int hdsp_wc_sync_check(struct hdsp *hdsp) @@ -3101,15 +3056,11 @@ static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ct static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"}; + static const char * const texts[] = { + "Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } @@ -3234,15 +3185,9 @@ static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"On", "Off"}; + static const char * const texts[] = {"On", "Off"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } @@ -3291,15 +3236,9 @@ static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"On", "Off"}; + static const char * const texts[] = {"On", "Off"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = { -- cgit v0.10.2 From c69a637b4df37fc5a011a89e422636ea393af5b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:41 +0200 Subject: ALSA: hdspm: Use snd_ctl_enum_info() ... and reduce the open codes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 52d86af..7f7277b 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2645,18 +2645,7 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 9; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - texts_freq[uinfo->value.enumerated.item+1]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 9, texts_freq + 1); } static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 7298ece7a26753b073a9ce5f979a4942d3904d44 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:19:53 +0200 Subject: ALSA: rme9652: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index fa9a2a8..6521521 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -920,15 +920,9 @@ static int rme9652_set_adat1_input(struct snd_rme9652 *rme9652, int internal) static int snd_rme9652_info_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = {"ADAT1", "Internal"}; + static const char * const texts[2] = {"ADAT1", "Internal"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_rme9652_get_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -991,15 +985,9 @@ static int rme9652_set_spdif_input(struct snd_rme9652 *rme9652, int in) static int snd_rme9652_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + static const char * const texts[3] = {"ADAT1", "Coaxial", "Internal"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme9652_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1140,15 +1128,11 @@ static int rme9652_set_sync_mode(struct snd_rme9652 *rme9652, int mode) static int snd_rme9652_info_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"AutoSync", "Master", "Word Clock"}; + static const char * const texts[3] = { + "AutoSync", "Master", "Word Clock" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme9652_get_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1231,16 +1215,14 @@ static int rme9652_set_sync_pref(struct snd_rme9652 *rme9652, int pref) static int snd_rme9652_info_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; + static const char * const texts[4] = { + "IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In" + }; struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, + rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3, + texts); } static int snd_rme9652_get_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1392,15 +1374,11 @@ static int snd_rme9652_get_spdif_rate(struct snd_kcontrol *kcontrol, struct snd_ static int snd_rme9652_info_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"}; + static const char * const texts[4] = { + "No Lock", "Lock", "No Lock Sync", "Lock Sync" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme9652_get_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 3e4bc5b78e5516585941c7888287ed50a5090bf4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:20:07 +0200 Subject: ALSA: sonicvibes: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 5b0d317..313a732 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -918,17 +918,11 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device, static int snd_sonicvibes_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[7] = { + static const char * const texts[7] = { "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= 7) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 7, texts); } static int snd_sonicvibes_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From 9883ab91e3ba5229bfe2d6e7f6ff497a2d03d4d2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:20:18 +0200 Subject: ALSA: via82xx: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index ecedf4d..e088467 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1610,16 +1610,10 @@ static int snd_via8233_capture_source_info(struct snd_kcontrol *kcontrol, /* formerly they were "Line" and "Mic", but it looks like that they * have nothing to do with the actual physical connections... */ - static char *texts[2] = { + static const char * const texts[2] = { "Input1", "Input2" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_via8233_capture_source_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 9502272163ace71d77d809937216fd669c02364b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:20:28 +0200 Subject: ALSA: ppc: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index b9ffc17..24c8766 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -795,16 +795,11 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix) static int snapper_info_capture_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { + static const char * const texts[2] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snapper_get_capture_source(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 5fe0b0e3ea1c8cb704677ef7e85345bb683f9182 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:15 +0200 Subject: ALSA: sparc: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to the text array. Signed-off-by: Takashi Iwai diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 4e91bca..06606f9 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1285,19 +1285,11 @@ static int snd_cs4231_timer(struct snd_card *card) static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "Line", "CD", "Mic", "Mix" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 2, 4, texts); } static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From c8dd33fc80cd344d28a1f6a7e1f0af1cf7a2ffd1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:27 +0200 Subject: ALSA: 6fire: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 184e398..54656ee 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -25,8 +25,8 @@ #include "comm.h" #include "chip.h" -static char *opt_coax_texts[2] = { "Optical", "Coax" }; -static char *line_phono_texts[2] = { "Line", "Phono" }; +static const char * const opt_coax_texts[2] = { "Optical", "Coax" }; +static const char * const line_phono_texts[2] = { "Line", "Phono" }; /* * data that needs to be sent to device. sets up card internal stuff. @@ -327,14 +327,7 @@ static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - line_phono_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts); } static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, @@ -361,14 +354,7 @@ static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - opt_coax_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts); } static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 7bbd03e0143b562ff7d96f7e71c016104020b550 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 20 Oct 2014 18:21:42 +0200 Subject: ALSA: usb-audio: Use snd_ctl_enum_info() ... and reduce the open codes. Also add missing const to text arrays. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f119a41..3980bf5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -437,19 +437,9 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[2] = {"1/2", - "3/4" - }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + static const char * const texts[2] = {"1/2", "3/4"}; - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, @@ -735,25 +725,12 @@ struct snd_ftu_eff_switch_priv_val { static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[8] = {"Room 1", - "Room 2", - "Room 3", - "Hall 1", - "Hall 2", - "Plate", - "Delay", - "Echo" + static const char *const texts[8] = { + "Room 1", "Room 2", "Room 3", "Hall 1", + "Hall 2", "Plate", "Delay", "Echo" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, -- cgit v0.10.2 From cf6814f2b5014ed5bbdef764a42e4abaa09b3a2e Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 21 Oct 2014 16:28:47 +0530 Subject: ALSA: ctxfi: remove unused variable As of now the pointer to struct dai is not being used anywhere in the function. So it is safe to remove the variable. If we are ever doing anything with the container_of(daio, struct dai, daio), then at that time we can again add the variable. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 4546590..632e843 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1145,7 +1145,6 @@ static int atc_release_resources(struct ct_atc *atc) int i; struct daio_mgr *daio_mgr = NULL; struct dao *dao = NULL; - struct dai *dai = NULL; struct daio *daio = NULL; struct sum_mgr *sum_mgr = NULL; struct src_mgr *src_mgr = NULL; @@ -1172,9 +1171,6 @@ static int atc_release_resources(struct ct_atc *atc) dao = container_of(daio, struct dao, daio); dao->ops->clear_left_input(dao); dao->ops->clear_right_input(dao); - } else { - dai = container_of(daio, struct dai, daio); - /* some thing to do for dai ... */ } daio_mgr->put_daio(daio_mgr, daio); } -- cgit v0.10.2 From c37de55efa1ccf018c27b876560725ff5e9f5701 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Oct 2014 12:01:13 +0200 Subject: ALSA: pcm: Remove arch-dependent mmap kludges Since we have consistently dma_mmap_coherent() for all architectures, the current ifdef and arch-specific codes in pcm core can be cleaned up gracefully. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 815396d..aa6754d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -35,9 +35,6 @@ #include #include #include -#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) -#include -#endif /* * Compatibility @@ -3251,20 +3248,6 @@ static inline struct page * snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) { void *vaddr = substream->runtime->dma_area + ofs; -#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) - return virt_to_page(CAC_ADDR(vaddr)); -#endif -#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) { - dma_addr_t addr = substream->runtime->dma_addr + ofs; - addr -= get_dma_offset(substream->dma_buffer.dev.dev); - /* assume dma_handle set via pfn_to_phys() in - * mm/dma-noncoherent.c - */ - return pfn_to_page(addr >> PAGE_SHIFT); - } -#endif return virt_to_page(vaddr); } @@ -3309,13 +3292,6 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { .fault = snd_pcm_mmap_data_fault, }; -#ifndef ARCH_HAS_DMA_MMAP_COHERENT -/* This should be defined / handled globally! */ -#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -#define ARCH_HAS_DMA_MMAP_COHERENT -#endif -#endif - /* * mmap the DMA buffer on RAM */ @@ -3331,7 +3307,6 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, area->vm_end - area->vm_start, area->vm_page_prot); } #endif /* CONFIG_GENERIC_ALLOCATOR */ -#ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) return dma_mmap_coherent(substream->dma_buffer.dev.dev, @@ -3339,11 +3314,6 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, substream->runtime->dma_area, substream->runtime->dma_addr, area->vm_end - area->vm_start); -#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV && - !plat_device_is_coherent(substream->dma_buffer.dev.dev)) - area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif /* ARCH_HAS_DMA_MMAP_COHERENT */ /* mmap with fault handler */ area->vm_ops = &snd_pcm_vm_ops_data_fault; return 0; -- cgit v0.10.2 From 63825f3a879ea2be569471643bb6aac73d9261f0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 22 Oct 2014 12:04:46 +0200 Subject: ALSA: pcm: Disable mmap for known broken archs Some architectures like PARISC is known not to support mmap properly with the DMA buffer, where dma_mmap_coherent() returns -EINVAL unconditionally. From the API POV, we should rather drop the mmap support there and expose it before the user-space tries to call mmap. The patch contains again ugly ifdef's, unfortunately, as there is no global flag indicating this. Once when such macro is defined, we can get rid of this instead. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index aa6754d..dc9a1355 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -192,6 +192,21 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, return err; } +static bool hw_support_mmap(struct snd_pcm_substream *substream) +{ + if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) + return false; + /* check architectures that return -EINVAL from dma_mmap_coherent() */ + /* FIXME: this should be some global flag */ +#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\ + defined(CONFIG_PARISC) || defined(CONFIG_XTENSA) + if (!substream->ops->mmap && + substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return false; +#endif + return true; +} + #undef RULES_DEBUG #ifdef RULES_DEBUG @@ -369,8 +384,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, } hw = &substream->runtime->hw; - if (!params->info) + if (!params->info) { params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; + if (!hw_support_mmap(substream)) + params->info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID); + } if (!params->fifo_size) { m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); @@ -2069,7 +2088,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; - if (hw->info & SNDRV_PCM_INFO_MMAP) { + if (hw_support_mmap(substream)) { if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) -- cgit v0.10.2 From 49d776ffb50f2e428aafb6a6576e58e80f1e886c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 Oct 2014 12:36:23 +0200 Subject: ALSA: pcm: Avoid mmap warnings on x86 On x86, using dma_mmap_coherent() for the pages allocated via dma_alloc_coherent() results in a warning like: aplay:32536 map pfn RAM range req uncached-minus for [mem 0x21d500000-0x21d51ffff], got write-back Until the issue is addressed in the core side, take back to the old good way in PCM code only for x86. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index dc9a1355..03e1e92 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3326,6 +3326,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, area->vm_end - area->vm_start, area->vm_page_prot); } #endif /* CONFIG_GENERIC_ALLOCATOR */ +#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */ if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) return dma_mmap_coherent(substream->dma_buffer.dev.dev, @@ -3333,6 +3334,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, substream->runtime->dma_area, substream->runtime->dma_addr, area->vm_end - area->vm_start); +#endif /* CONFIG_X86 */ /* mmap with fault handler */ area->vm_ops = &snd_pcm_vm_ops_data_fault; return 0; -- cgit v0.10.2 From 66797f36fd17e8975f4a3449aed895cda952c0ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 27 Oct 2014 15:15:44 +0100 Subject: ALSA: hda - Pass printf argument directly to request_module() request_module() handles the printf style arguments, so we don't have to render strings in the caller side. Not only it reduces the unnecessary temporary string buffer, it's even safer from the security POV. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 259fbea..0025bf4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -966,14 +966,12 @@ find_codec_preset(struct hda_codec *codec) mutex_unlock(&preset_mutex); if (mod_requested < HDA_MODREQ_MAX_COUNT) { - char name[32]; if (!mod_requested) - snprintf(name, sizeof(name), "snd-hda-codec-id:%08x", - codec->vendor_id); + request_module("snd-hda-codec-id:%08x", + codec->vendor_id); else - snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*", - (codec->vendor_id >> 16) & 0xffff); - request_module(name); + request_module("snd-hda-codec-id:%04x*", + (codec->vendor_id >> 16) & 0xffff); mod_requested++; goto again; } -- cgit v0.10.2 From 326f0480b7f8504c4f594c4f36ab7874e17780bc Mon Sep 17 00:00:00 2001 From: Aya Mahfouz Date: Tue, 28 Oct 2014 14:27:44 +0200 Subject: ALSA: pcxhr: convert timeval to ktime_t This patch is concerned with migrating the time variables in the pcxhr module found in the sound driver. The changes are concerend with the y2038 problem where timeval will overflow in the year 2038. ktime_t was used instead of timeval to get the wall time. The difference is displayed now in nanoseconds instead of microseconds. Signed-off-by: Aya Mahfouz Reviewed-by: Arnd Bergmann Signed-off-by: Takashi Iwai diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index b854fc5..7c33c97 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -711,8 +711,9 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) int playback_mask = 0; #ifdef CONFIG_SND_DEBUG_VERBOSE - struct timeval my_tv1, my_tv2; - do_gettimeofday(&my_tv1); + ktime_t start_time, stop_time, diff_time; + + start_time = ktime_get(); #endif mutex_lock(&mgr->setup_mutex); @@ -823,9 +824,10 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) mutex_unlock(&mgr->setup_mutex); #ifdef CONFIG_SND_DEBUG_VERBOSE - do_gettimeofday(&my_tv2); + stop_time = ktime_get(); + diff_time = ktime_sub(stop_time, start_time); dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n", - (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); + (long)(ktime_to_ns(diff_time)), err); #endif } diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index a584acb..181f772 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -910,8 +910,9 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int audio_mask; #ifdef CONFIG_SND_DEBUG_VERBOSE - struct timeval my_tv1, my_tv2; - do_gettimeofday(&my_tv1); + ktime_t start_time, stop_time, diff_time; + + start_time = ktime_get(); #endif audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET)); @@ -960,9 +961,10 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, return err; } #ifdef CONFIG_SND_DEBUG_VERBOSE - do_gettimeofday(&my_tv2); + stop_time = ktime_get(); + diff_time = ktime_sub(stop_time, start_time); dev_dbg(&mgr->pci->dev, "***SET PIPE STATE*** TIME = %ld (err = %x)\n", - (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); + (long)(ktime_to_ns(diff_time)), err); #endif return 0; } -- cgit v0.10.2 From 90446d0746c399e47246232f66ff2fa8616ba470 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Oct 2014 15:28:07 +0100 Subject: ALSA: doc: Add missing headers and compress stuff to alsa-driver-api.tmpl Some header files have kereldoc comments but are not referred properly. Let's add them. Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl index 0230a96..13f8b24 100644 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -57,6 +57,7 @@ !Esound/core/pcm.c !Esound/core/pcm_lib.c !Esound/core/pcm_native.c +!include/sound/pcm.h PCM Format Helpers !Esound/core/pcm_misc.c @@ -64,6 +65,10 @@ PCM Memory Management !Esound/core/pcm_memory.c + PCM DMA Engine API +!Esound/core/pcm_dmaengine.c +!Iinclude/sound/dmaengine_pcm.h + Control/Mixer API General Control Interface @@ -91,12 +96,19 @@ !Esound/core/info.c + Compress Offload + Compress Offload API +!Esound/core/compress_offload.c +!Iinclude/sound/compress_driver.h + + Miscellaneous Functions Hardware-Dependent Devices API !Esound/core/hwdep.c Jack Abstraction Layer API !Esound/core/jack.c +!Iinclude/sound/jack.h ISA DMA Helpers !Esound/core/isadma.c -- cgit v0.10.2 From 7b366d5f161c2a69eeafe525105a5a9277982472 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Oct 2014 15:30:00 +0100 Subject: ALSA: jack: Fix kerneldoc comments Signed-off-by: Takashi Iwai diff --git a/include/sound/jack.h b/include/sound/jack.h index 5891657..37e612e 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -28,8 +28,9 @@ struct input_dev; /** - * Jack types which can be reported. These values are used as a - * bitmask. + * enum snd_jack_types: Jack types which can be reported + * + * These values are used as a bitmask. * * Note that this must be kept in sync with the lookup table in * sound/core/jack.c. -- cgit v0.10.2 From 859b2e374a86482004d1b8b94c1666269e1d7fd6 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 28 Oct 2014 21:25:13 +0530 Subject: ALSA: compress: fix documentation errors Some structure documentation was not right so fix it now Reported-by: kbuild test robot Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index ae6c3b8..396e8f7 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -42,12 +42,11 @@ struct snd_compr_ops; * @buffer_size: size of the above buffer * @fragment_size: size of buffer fragment in bytes * @fragments: number of such fragments - * @hw_pointer: offset of last location in buffer where DSP copied data - * @app_pointer: offset of last location in buffer where app wrote data * @total_bytes_available: cumulative number of bytes made available in * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep + * @private_data: driver private data pointer */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -94,6 +93,8 @@ struct snd_compr_stream { * This can be called in during stream creation only to set codec params * and the stream properties * @get_params: retrieve the codec parameters, mandatory + * @set_metadata: Set the metadata values for a stream + * @get_metadata: retreives the requested metadata values from stream * @trigger: Trigger operations like start, pause, resume, drain, stop. * This callback is mandatory * @pointer: Retrieve current h/w pointer information. Mandatory -- cgit v0.10.2 From 2e6705c09065ecb357140e44d12dc32274b1a723 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Oct 2014 17:16:51 +0100 Subject: ALSA: ctxfi: Kill the rest snd_print*() Use the standard dev_*() instead. Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 632e843..977a598 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -438,7 +438,9 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) position = src->ops->get_ca(src); if (position < apcm->vm_block->addr) { - snd_printdd("ctxfi: bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n", position, apcm->vm_block->addr, apcm->vm_block->size); + dev_dbg(atc->card->dev, + "bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n", + position, apcm->vm_block->addr, apcm->vm_block->size); position = apcm->vm_block->addr; } @@ -1295,7 +1297,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid) atc->model = CT20K2_UNKNOWN; } atc->model_name = ct_subsys_name[atc->model]; - snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", + dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n", atc->chip_name, atc->model_name, vendor_id, device_id); return 0; diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 03fb909..a5d4604 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -421,12 +421,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) atimer->atc = atc; hw = atc->hw; if (!use_system_timer && hw->set_timer_irq) { - snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n"); + dev_info(atc->card->dev, "Use xfi-native timer\n"); atimer->ops = &ct_xfitimer_ops; hw->irq_callback_data = atimer; hw->irq_callback = ct_timer_interrupt; } else { - snd_printd(KERN_INFO "ctxfi: Use system timer\n"); + dev_info(atc->card->dev, "Use system timer\n"); atimer->ops = &ct_systimer_ops; } return atimer; -- cgit v0.10.2 From f48a6df28239f5bf35d80e43e580261d2298395a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Oct 2014 17:22:45 +0100 Subject: ALSA: pcxhr: Kill the rest snd_print*() Use the standard dev_*() instead. Signed-off-by: Takashi Iwai diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 7c33c97..a602930 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -501,10 +501,10 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, /* * start or stop playback/capture substream */ -static int pcxhr_set_stream_state(struct pcxhr_stream *stream) +static int pcxhr_set_stream_state(struct snd_pcxhr *chip, + struct pcxhr_stream *stream) { int err; - struct snd_pcxhr *chip; struct pcxhr_rmh rmh; int stream_mask, start; @@ -512,8 +512,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream) start = 1; else { if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) { - snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state " - "CANNOT be stopped\n"); + dev_err(chip->card->dev, + "pcxhr_set_stream_state CANNOT be stopped\n"); return -EINVAL; } start = 0; @@ -560,6 +560,7 @@ static int pcxhr_set_format(struct pcxhr_stream *stream) struct pcxhr_rmh rmh; unsigned int header; + chip = snd_pcm_substream_chip(stream->substream); switch (stream->format) { case SNDRV_PCM_FORMAT_U8: header = HEADER_FMT_BASE_LIN; @@ -582,11 +583,10 @@ static int pcxhr_set_format(struct pcxhr_stream *stream) header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL; break; default: - snd_printk(KERN_ERR - "error pcxhr_set_format() : unknown format\n"); + dev_err(chip->card->dev, + "error pcxhr_set_format() : unknown format\n"); return -EINVAL; } - chip = snd_pcm_substream_chip(stream->substream); sample_rate = chip->mgr->sample_rate; if (sample_rate <= 32000 && sample_rate !=0) { @@ -643,11 +643,11 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE); stream_num = is_capture ? 0 : subs->number; - snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : " - "addr(%p) bytes(%zx) subs(%d)\n", - is_capture ? 'c' : 'p', - chip->chip_idx, (void *)(long)subs->runtime->dma_addr, - subs->runtime->dma_bytes, subs->number); + dev_dbg(chip->card->dev, + "pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", + is_capture ? 'c' : 'p', + chip->chip_idx, (void *)(long)subs->runtime->dma_addr, + subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, @@ -687,7 +687,7 @@ static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, *sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24; *sample_count += (snd_pcm_uframes_t)rmh.stat[1]; } - snd_printdd("PIPE_SAMPLE_COUNT = %lx\n", *sample_count); + dev_dbg(chip->card->dev, "PIPE_SAMPLE_COUNT = %lx\n", *sample_count); return err; } #endif @@ -779,12 +779,12 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) for (j = 0; j < chip->nb_streams_capt; j++) { stream = &chip->capture_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) - err = pcxhr_set_stream_state(stream); + err = pcxhr_set_stream_state(chip, stream); } for (j = 0; j < chip->nb_streams_play; j++) { stream = &chip->playback_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) - err = pcxhr_set_stream_state(stream); + err = pcxhr_set_stream_state(chip, stream); } } @@ -839,12 +839,12 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) { struct pcxhr_stream *stream; struct snd_pcm_substream *s; + struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_printdd("SNDRV_PCM_TRIGGER_START\n"); + dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_START\n"); if (snd_pcm_stream_linked(subs)) { - struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); snd_pcm_group_for_each_entry(s, subs) { if (snd_pcm_substream_chip(s) != chip) continue; @@ -856,7 +856,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) pcxhr_start_linked_stream(chip->mgr); } else { stream = subs->runtime->private_data; - snd_printdd("Only one Substream %c %d\n", + dev_dbg(chip->card->dev, "Only one Substream %c %d\n", stream->pipe->is_capture ? 'C' : 'P', stream->pipe->first_audio); if (pcxhr_set_format(stream)) @@ -865,17 +865,17 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) return -EINVAL; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN; - if (pcxhr_set_stream_state(stream)) + if (pcxhr_set_stream_state(chip, stream)) return -EINVAL; stream->status = PCXHR_STREAM_STATUS_RUNNING; } break; case SNDRV_PCM_TRIGGER_STOP: - snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); + dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_STOP\n"); snd_pcm_group_for_each_entry(s, subs) { stream = s->runtime->private_data; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP; - if (pcxhr_set_stream_state(stream)) + if (pcxhr_set_stream_state(chip, stream)) return -EINVAL; snd_pcm_trigger_done(s, subs); } -- cgit v0.10.2 From f994cb3a09a5f2018c286f854c10277132f4a9a5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Oct 2014 17:36:50 +0100 Subject: ALSA: au88x0: Kill the rest snd_print*() Use the standard dev_*() instead. Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index e9c3833..9963691 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -314,7 +314,7 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH, sizeof(snd_vortex_synth_arg_t), &wave) < 0 || wave == NULL) { - snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n"); + dev_err(card->dev, "Can't initialize Aureal wavetable synth\n"); } else { snd_vortex_synth_arg_t *arg; -- cgit v0.10.2 From 885e7b0e181c14e4d0ddd26c688bad2b84c1ada9 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 14 Oct 2014 14:16:24 -0700 Subject: target: Don't call TFO->write_pending if data_length == 0 If an initiator sends a zero-length command (e.g. TEST UNIT READY) but sets the transfer direction in the transport layer to indicate a data-out phase, we still shouldn't try to transfer data. At best it's a NOP, and depending on the transport, we might crash on an uninitialized sg list. Reported-by: Craig Watson Signed-off-by: Roland Dreier Cc: # 3.1 Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 9ea0d5f..be877bf 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2292,7 +2292,7 @@ transport_generic_new_cmd(struct se_cmd *cmd) * and let it call back once the write buffers are ready. */ target_add_to_state_list(cmd); - if (cmd->data_direction != DMA_TO_DEVICE) { + if (cmd->data_direction != DMA_TO_DEVICE || cmd->data_length == 0) { target_execute_cmd(cmd); return 0; } -- cgit v0.10.2 From ab8edab132829b26dd13db6caca3c242cce35dc1 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 8 Oct 2014 06:19:20 +0000 Subject: vhost-scsi: Take configfs group dependency during VHOST_SCSI_SET_ENDPOINT This patch addresses a bug where individual vhost-scsi configfs endpoint groups can be removed from below while active exports to QEMU userspace still exist, resulting in an OOPs. It adds a configfs_depend_item() in vhost_scsi_set_endpoint() to obtain an explicit dependency on se_tpg->tpg_group in order to prevent individual vhost-scsi WWPN endpoints from being released via normal configfs methods while an QEMU ioctl reference still exists. Also, add matching configfs_undepend_item() in vhost_scsi_clear_endpoint() to release the dependency, once QEMU's reference to the individual group at /sys/kernel/config/target/vhost/$WWPN/$TPGT is released. (Fix up vhost_scsi_clear_endpoint() error path - DanC) Cc: Michael S. Tsirkin Cc: Paolo Bonzini Cc: Stefan Hajnoczi Cc: # 3.6+ Signed-off-by: Nicholas Bellinger diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 69906ca..a17f118 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1312,6 +1312,7 @@ static int vhost_scsi_set_endpoint(struct vhost_scsi *vs, struct vhost_scsi_target *t) { + struct se_portal_group *se_tpg; struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tpg; struct tcm_vhost_tpg **vs_tpg; @@ -1359,6 +1360,21 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, ret = -EEXIST; goto out; } + /* + * In order to ensure individual vhost-scsi configfs + * groups cannot be removed while in use by vhost ioctl, + * go ahead and take an explicit se_tpg->tpg_group.cg_item + * dependency now. + */ + se_tpg = &tpg->se_tpg; + ret = configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys, + &se_tpg->tpg_group.cg_item); + if (ret) { + pr_warn("configfs_depend_item() failed: %d\n", ret); + kfree(vs_tpg); + mutex_unlock(&tpg->tv_tpg_mutex); + goto out; + } tpg->tv_tpg_vhost_count++; tpg->vhost_scsi = vs; vs_tpg[tpg->tport_tpgt] = tpg; @@ -1401,6 +1417,7 @@ static int vhost_scsi_clear_endpoint(struct vhost_scsi *vs, struct vhost_scsi_target *t) { + struct se_portal_group *se_tpg; struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tpg; struct vhost_virtqueue *vq; @@ -1449,6 +1466,13 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, vs->vs_tpg[target] = NULL; match = true; mutex_unlock(&tpg->tv_tpg_mutex); + /* + * Release se_tpg->tpg_group.cg_item configfs dependency now + * to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur. + */ + se_tpg = &tpg->se_tpg; + configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys, + &se_tpg->tpg_group.cg_item); } if (match) { for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { -- cgit v0.10.2 From f2774f430ea65fddc068865d364bb254d2816648 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 15 Oct 2014 10:59:21 -0700 Subject: iscsi-target: return the correct port in SendTargets The fact that a target is published on the any address has no bearing on which port(s) it is published. SendTargets should always send the portal's port, not the port used for discovery. Signed-off-by: Steven Allen Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index b19e432..73e58d2 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3491,7 +3491,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, len = sprintf(buf, "TargetAddress=" "%s:%hu,%hu", inaddr_any ? conn->local_ip : np->np_ip, - inaddr_any ? conn->local_port : np->np_port, + np->np_port, tpg->tpgt); len += 1; -- cgit v0.10.2 From ab477c1ff5e0a744c072404bf7db51bfe1f05b6e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sun, 19 Oct 2014 18:05:33 +0300 Subject: srp-target: Retry when QP creation fails with ENOMEM It is not guaranteed to that srp_sq_size is supported by the HCA. So if we failed to create the QP with ENOMEM, try with a smaller srp_sq_size. Keep it up until we hit MIN_SRPT_SQ_SIZE, then fail the connection. Reported-by: Mark Lehrer Signed-off-by: Bart Van Assche Signed-off-by: Sagi Grimberg Cc: # 3.4+ Signed-off-by: Nicholas Bellinger diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 7206547..dc82968 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -2092,6 +2092,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) if (!qp_init) goto out; +retry: ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch, ch->rq_size + srp_sq_size, 0); if (IS_ERR(ch->cq)) { @@ -2115,6 +2116,13 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) ch->qp = ib_create_qp(sdev->pd, qp_init); if (IS_ERR(ch->qp)) { ret = PTR_ERR(ch->qp); + if (ret == -ENOMEM) { + srp_sq_size /= 2; + if (srp_sq_size >= MIN_SRPT_SQ_SIZE) { + ib_destroy_cq(ch->cq); + goto retry; + } + } printk(KERN_ERR "failed to create_qp ret= %d\n", ret); goto err_destroy_cq; } -- cgit v0.10.2 From e83280f96f108ee7af8c5669080cf664b0c2b8fb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Oct 2014 08:20:46 +0100 Subject: ALSA: doc: Fix enum snd_jack_types comments Follow the proper kerneldoc rule, and complete enum item comments. Signed-off-by: Takashi Iwai diff --git a/include/sound/jack.h b/include/sound/jack.h index 37e612e..67f2bbc 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -28,7 +28,21 @@ struct input_dev; /** - * enum snd_jack_types: Jack types which can be reported + * enum snd_jack_types - Jack types which can be reported + * @SND_JACK_HEADPHONE: Headphone + * @SND_JACK_MICROPHONE: Microphone + * @SND_JACK_HEADSET: Headset + * @SND_JACK_LINEOUT: Line out + * @SND_JACK_MECHANICAL: Mechanical switch + * @SND_JACK_VIDEOOUT: Video out + * @SND_JACK_AVOUT: AV (Audio Video) out + * @SND_JACK_LINEIN: Line in + * @SND_JACK_BTN_0: Button 0 + * @SND_JACK_BTN_1: Button 1 + * @SND_JACK_BTN_2: Button 2 + * @SND_JACK_BTN_3: Button 3 + * @SND_JACK_BTN_4: Button 4 + * @SND_JACK_BTN_5: Button 5 * * These values are used as a bitmask. * -- cgit v0.10.2 From f533ccb61edf008e14c9e1b91b48cd2c7397f33d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Oct 2014 08:22:05 +0100 Subject: ALSA: doc: Fix uapi/sound/compress_offload.h kerneldoc comments so that make htmldocs works properly. Since kerneldoc can't handle noname enum properly, name enum sndrv_compress_encoder. Signed-off-by: Takashi Iwai diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 1964026..22ed8cb 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -32,7 +32,7 @@ #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2) /** - * struct snd_compressed_buffer: compressed buffer + * struct snd_compressed_buffer - compressed buffer * @fragment_size: size of buffer fragment in bytes * @fragments: number of such fragments */ @@ -42,7 +42,7 @@ struct snd_compressed_buffer { } __attribute__((packed, aligned(4))); /** - * struct snd_compr_params: compressed stream params + * struct snd_compr_params - compressed stream params * @buffer: buffer description * @codec: codec parameters * @no_wake_mode: dont wake on fragment elapsed @@ -54,7 +54,7 @@ struct snd_compr_params { } __attribute__((packed, aligned(4))); /** - * struct snd_compr_tstamp: timestamp descriptor + * struct snd_compr_tstamp - timestamp descriptor * @byte_offset: Byte offset in ring buffer to DSP * @copied_total: Total number of bytes copied from/to ring buffer to/by DSP * @pcm_frames: Frames decoded or encoded by DSP. This field will evolve by @@ -73,7 +73,7 @@ struct snd_compr_tstamp { } __attribute__((packed, aligned(4))); /** - * struct snd_compr_avail: avail descriptor + * struct snd_compr_avail - avail descriptor * @avail: Number of bytes available in ring buffer for writing/reading * @tstamp: timestamp infomation */ @@ -88,7 +88,7 @@ enum snd_compr_direction { }; /** - * struct snd_compr_caps: caps descriptor + * struct snd_compr_caps - caps descriptor * @codecs: pointer to array of codecs * @direction: direction supported. Of type snd_compr_direction * @min_fragment_size: minimum fragment supported by DSP @@ -110,7 +110,7 @@ struct snd_compr_caps { } __attribute__((packed, aligned(4))); /** - * struct snd_compr_codec_caps: query capability of codec + * struct snd_compr_codec_caps - query capability of codec * @codec: codec for which capability is queried * @num_descriptors: number of codec descriptors * @descriptor: array of codec capability descriptor @@ -122,18 +122,19 @@ struct snd_compr_codec_caps { } __attribute__((packed, aligned(4))); /** + * enum sndrv_compress_encoder * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the * end of the track * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the * beginning of the track */ -enum { +enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2, }; /** - * struct snd_compr_metadata: compressed stream metadata + * struct snd_compr_metadata - compressed stream metadata * @key: key id * @value: key value */ -- cgit v0.10.2 From 00dad6cfd25b75cc9cc24b8abfc263177e581ae2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Oct 2014 08:23:31 +0100 Subject: ALSA: doc: Include uapi/sound/compress_*.h, too Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl index 13f8b24..7bec270 100644 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -99,6 +99,8 @@ Compress Offload Compress Offload API !Esound/core/compress_offload.c +!Iinclude/uapi/sound/compress_offload.h +!Iinclude/uapi/sound/compress_params.h !Iinclude/sound/compress_driver.h -- cgit v0.10.2 From a11e9b168646cfc5d3b8d605d430d7e4ff267d72 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Oct 2014 15:06:01 +0100 Subject: ALSA: hda - Correct kerneldoc comments Complete the missing parameters and fix anything wrong there. Just comment changes. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index fcc5e47..7388958 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -464,8 +464,12 @@ EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); /** * hda_get_input_pin_label - Give a label for the given input pin + * @codec: the HDA codec + * @item: ping config item to refer + * @pin: the pin NID + * @check_location: flag to add the jack location prefix * - * When check_location is true, the function checks the pin location + * When @check_location is true, the function checks the pin location * for mic and line-in pins, and set an appropriate prefix like "Front", * "Rear", "Internal". */ @@ -550,6 +554,9 @@ static int check_mic_location_need(struct hda_codec *codec, /** * hda_get_autocfg_input_label - Get a label for the given input + * @codec: the HDA codec + * @cfg: the parsed pin configuration + * @input: the input index number * * Get a label for the given input pin defined by the autocfg item. * Unlike hda_get_input_pin_label(), this function checks all inputs @@ -677,6 +684,12 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_get_pin_label - Get a label for the given I/O pin + * @codec: the HDA codec + * @nid: pin NID + * @cfg: the parsed pin configuration + * @label: the string buffer to store + * @maxlen: the max length of string buffer (including termination) + * @indexp: the pointer to return the index number (for multiple ctls) * * Get a label for the given pin. This function works for both input and * output pins. When @cfg is given as non-NULL, the function tries to get diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0025bf4..e152542 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -416,7 +416,6 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) * snd_hda_get_conn_list - get connection list * @codec: the HDA codec * @nid: NID to parse - * @len: number of connection list entries * @listp: the pointer to store NID list * * Parses the connection list of the given widget and stores the pointer @@ -2004,6 +2003,7 @@ EXPORT_SYMBOL_GPL(query_amp_caps); * @codec: the HD-audio codec * @nid: the NID to query * @dir: either #HDA_INPUT or #HDA_OUTPUT + * @bits: bit mask to check the result * * Check whether the widget has the given amp capability for the direction. */ @@ -2023,7 +2023,7 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); * snd_hda_override_amp_caps - Override the AMP capabilities * @codec: the CODEC to clean up * @nid: the NID to clean up - * @direction: either #HDA_INPUT or #HDA_OUTPUT + * @dir: either #HDA_INPUT or #HDA_OUTPUT * @caps: the capability bits to set * * Override the cached AMP caps bits value by the given one. @@ -2320,6 +2320,8 @@ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, /** * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2381,6 +2383,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2406,6 +2410,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get); /** * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2436,6 +2442,10 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); /** * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @op_flag: operation flag + * @size: byte size of input TLV + * @_tlv: TLV data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3012,6 +3022,8 @@ EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook); /** * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3031,6 +3043,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info); /** * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3057,6 +3071,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get); /** * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3100,6 +3116,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); /** * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_MUTE*() macros. @@ -3123,6 +3141,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_get); /** * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_MUTE*() macros. @@ -3153,6 +3173,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_put); /** * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3176,6 +3198,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_info); /** * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3199,6 +3223,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_get); /** * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3228,6 +3254,10 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_put); /** * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control + * @kcontrol: ctl element + * @op_flag: operation flag + * @size: byte size of input TLV + * @tlv: TLV data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() macro. @@ -4305,6 +4335,7 @@ static struct hda_rate_tbl rate_bits[] = { * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * @@ -4980,6 +5011,7 @@ static void __snd_hda_power_down(struct hda_codec *codec) * snd_hda_power_save - Power-up/down/sync the codec * @codec: HD-audio codec * @delta: the counter delta to change + * @d3wait: sync for D3 transition complete * * Change the power-up counter via @delta, and power up or down the hardware * appropriately. For the power-down, queue to the delayed action. @@ -5055,6 +5087,10 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); /** * snd_hda_ch_mode_info - Info callback helper for the channel mode enum + * @codec: the HDA codec + * @uinfo: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size */ int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, @@ -5074,6 +5110,11 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info); /** * snd_hda_ch_mode_get - Get callback helper for the channel mode enum + * @codec: the HDA codec + * @ucontrol: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size + * @max_channels: max number of channels */ int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -5095,6 +5136,11 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get); /** * snd_hda_ch_mode_put - Put callback helper for the channel mode enum + * @codec: the HDA codec + * @ucontrol: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size + * @max_channelsp: pointer to store the max channels */ int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -5123,6 +5169,8 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put); /** * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum + * @imux: imux helper object + * @uinfo: pointer to get/store the data */ int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo) @@ -5144,6 +5192,11 @@ EXPORT_SYMBOL_GPL(snd_hda_input_mux_info); /** * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum + * @codec: the HDA codec + * @imux: imux helper object + * @ucontrol: pointer to get/store the data + * @nid: input mux NID + * @cur_val: pointer to get/store the current imux value */ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, @@ -5168,7 +5221,13 @@ int snd_hda_input_mux_put(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_input_mux_put); -/* +/** + * snd_hda_enum_helper_info - Helper for simple enum ctls + * @kcontrol: ctl element + * @uinfo: pointer to get/store the data + * @num_items: number of enum items + * @texts: enum item string array + * * process kcontrol info callback of a simple string enum array * when @num_items is 0 or @texts is NULL, assume a boolean enum array */ @@ -5257,6 +5316,8 @@ EXPORT_SYMBOL_GPL(snd_hda_bus_reboot_notify); /** * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5273,6 +5334,11 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open); /** * snd_hda_multi_out_dig_prepare - prepare the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object + * @stream_tag: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign */ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -5289,6 +5355,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare); /** * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5302,6 +5370,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup); /** * snd_hda_multi_out_dig_close - release the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5315,6 +5385,10 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close); /** * snd_hda_multi_out_analog_open - open analog outputs + * @codec: the HDA codec + * @mout: hda_multi_out object + * @substream: PCM substream to assign + * @hinfo: PCM information to assign * * Open analog outputs and set up the hw-constraints. * If the digital outputs can be opened as slave, open the digital @@ -5365,6 +5439,11 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open); /** * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. + * @codec: the HDA codec + * @mout: hda_multi_out object + * @stream_tag: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign * * Set up the i/o for analog out. * When the digital out is available, copy the front out to digital out, too. @@ -5442,6 +5521,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare); /** * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5473,6 +5554,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup); /** * snd_hda_get_default_vref - Get the default (mic) VREF pin bits + * @codec: the HDA codec + * @pin: referred pin NID * * Guess the suitable VREF pin bits to be set as the pin-control value. * Note: the function doesn't set the AC_PINCTL_IN_EN bit. @@ -5498,7 +5581,12 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) } EXPORT_SYMBOL_GPL(snd_hda_get_default_vref); -/* correct the pin ctl value for matching with the pin cap */ +/** + * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap + * @codec: the HDA codec + * @pin: referred pin NID + * @val: pin ctl value to audit + */ unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) { @@ -5549,6 +5637,19 @@ unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); +/** + * _snd_hda_pin_ctl - Helper to set pin ctl value + * @codec: the HDA codec + * @pin: referred pin NID + * @val: pin control value to set + * @cached: access over codec pinctl cache or direct write + * + * This function is a helper to set a pin ctl value more safely. + * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the + * value in pin target array via snd_hda_codec_set_pin_target(), then + * actually writes the value via either snd_hda_codec_update_cache() or + * snd_hda_codec_write() depending on @cached flag. + */ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached) { @@ -5565,6 +5666,11 @@ EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl); /** * snd_hda_add_imux_item - Add an item to input_mux + * @codec: the HDA codec + * @imux: imux helper object + * @label: the name of imux item to assign + * @index: index number of imux item to assign + * @type_idx: pointer to store the resultant label index * * When the same label is used already in the existing items, the number * suffix is appended to the label. This label index number is stored diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index e1cd34d..0e6d753 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -371,7 +371,7 @@ error: return ret; } -/** +/* * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with * hdmi-specific routine. */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index f56765a..b2d81ab 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -57,6 +57,8 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) /** * snd_hda_jack_tbl_get - query the jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to refer to */ struct hda_jack_tbl * snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) @@ -75,6 +77,8 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get); /** * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag + * @codec: the HDA codec + * @tag: tag value to refer to */ struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag) @@ -93,6 +97,8 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag); /** * snd_hda_jack_tbl_new - create a jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to assign */ static struct hda_jack_tbl * snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) @@ -162,6 +168,7 @@ static void jack_detect_update(struct hda_codec *codec, /** * snd_hda_set_dirty_all - Mark all the cached as dirty + * @codec: the HDA codec * * This function sets the dirty flag to all entries of jack table. * It's called from the resume path in hda_codec.c. @@ -218,6 +225,9 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state); /** * snd_hda_jack_detect_enable - enable the jack-detection + * @codec: the HDA codec + * @nid: pin NID to enable + * @func: callback function to register * * In the case of error, the return value will be a pointer embedded with * errno. Check and handle the return value appropriately with standard @@ -266,6 +276,9 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); /** * snd_hda_jack_set_gating_jack - Set gating jack. + * @codec: the HDA codec + * @gated_nid: gated pin NID + * @gating_nid: gating pin NID * * Indicates the gated jack is only valid when the gating jack is plugged. */ @@ -287,6 +300,7 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); /** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed + * @codec: the HDA codec */ void snd_hda_jack_report_sync(struct hda_codec *codec) { @@ -349,6 +363,11 @@ static void hda_free_jack_priv(struct snd_jack *jack) /** * snd_hda_jack_add_kctl - Add a kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID to assign + * @name: string name for the jack + * @idx: index number for the jack + * @phantom_jack: flag to deal as a phantom jack * * This assigns a jack-detection kctl to the given pin. The kcontrol * will have the given name and index. @@ -456,6 +475,8 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg + * @codec: the HDA codec + * @cfg: pin config table to parse */ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) -- cgit v0.10.2 From 95a962c36f6e3c3edb438d1ba59e30964900d16a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 Oct 2014 16:03:58 +0100 Subject: ALSA: hda - More kerneldoc comments Put more kerneldoc comments to the exported functions. Still the generic parser code and the HD-audio controller code aren't covered yet, though. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7388958..1ede822 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -441,6 +441,13 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg); +/** + * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config + * @def_conf: pin configuration value + * + * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given + * default pin configuration value. + */ int snd_hda_get_input_pin_attr(unsigned int def_conf) { unsigned int loc = get_defcfg_location(def_conf); @@ -473,7 +480,6 @@ EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); * for mic and line-in pins, and set an appropriate prefix like "Front", * "Rear", "Internal". */ - static const char *hda_get_input_pin_label(struct hda_codec *codec, const struct auto_pin_cfg_item *item, hda_nid_t pin, bool check_location) @@ -761,6 +767,14 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_get_pin_label); +/** + * snd_hda_add_verbs - Add verbs to the init list + * @codec: the HDA codec + * @list: zero-terminated verb list to add + * + * Append the given verb list to the execution list. The verbs will be + * performed at init and resume time via snd_hda_apply_verbs(). + */ int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list) { @@ -773,6 +787,10 @@ int snd_hda_add_verbs(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_verbs); +/** + * snd_hda_apply_verbs - Execute the init verb lists + * @codec: the HDA codec + */ void snd_hda_apply_verbs(struct hda_codec *codec) { int i; @@ -783,6 +801,11 @@ void snd_hda_apply_verbs(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_apply_verbs); +/** + * snd_hda_apply_pincfgs - Set each pin config in the given list + * @codec: the HDA codec + * @cfg: NULL-terminated pin config table + */ void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) { @@ -850,6 +873,11 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) } } +/** + * snd_hda_apply_fixup - Apply the fixup chain with the given action + * @codec: the HDA codec + * @action: fixup action (HDA_FIXUP_ACT_XXX) + */ void snd_hda_apply_fixup(struct hda_codec *codec, int action) { if (codec->fixup_list) @@ -868,6 +896,12 @@ static bool pin_config_match(struct hda_codec *codec, return true; } +/** + * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list + * @codec: the HDA codec + * @pin_quirk: zero-terminated pin quirk list + * @fixlist: the fixup list + */ void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, const struct hda_fixup *fixlist) @@ -894,6 +928,21 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); +/** + * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string + * @codec: the HDA codec + * @models: NULL-terminated model string list + * @quirk: zero-terminated PCI/codec SSID quirk list + * @fixlist: the fixup list + * + * Pick up a fixup entry matching with the given model string or SSID. + * If a fixup was already set beforehand, the function doesn't do anything. + * When a special model string "nofixup" is given, also no fixup is applied. + * + * The function tries to find the matching model name at first, if given. + * If nothing matched, try to look up the PCI SSID. + * If still nothing matched, try to look up the codec SSID. + */ void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, const struct snd_pci_quirk *quirk, diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 8c6c50a..1e7de08 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -175,6 +175,11 @@ static int snd_hda_do_attach(struct hda_beep *beep) return 0; } +/** + * snd_hda_enable_beep_device - Turn on/off beep sound + * @codec: the HDA codec + * @enable: flag to turn on/off + */ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) { struct hda_beep *beep = codec->beep; @@ -191,6 +196,20 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) } EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); +/** + * snd_hda_attach_beep_device - Attach a beep input device + * @codec: the HDA codec + * @nid: beep NID + * + * Attach a beep object to the given widget. If beep hint is turned off + * explicitly or beep_mode of the codec is turned off, this doesn't nothing. + * + * The attached beep device has to be registered via + * snd_hda_register_beep_device() and released via snd_hda_detach_beep_device() + * appropriately. + * + * Currently, only one beep device is allowed to each codec. + */ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) { struct hda_beep *beep; @@ -228,6 +247,10 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) } EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device); +/** + * snd_hda_detach_beep_device - Detach the beep device + * @codec: the HDA codec + */ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; @@ -240,6 +263,10 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device); +/** + * snd_hda_register_beep_device - Register the beep device + * @codec: the HDA codec + */ int snd_hda_register_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; @@ -269,6 +296,12 @@ static bool ctl_has_mute(struct snd_kcontrol *kcontrol) } /* get/put callbacks for beep mute mixer switches */ + +/** + * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data + */ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -283,6 +316,11 @@ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep); +/** + * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data + */ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e152542..ca98f52 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -77,6 +77,10 @@ static struct hda_vendor_id hda_vendor_ids[] = { static DEFINE_MUTEX(preset_mutex); static LIST_HEAD(hda_preset_tables); +/** + * snd_hda_add_codec_preset - Add a codec preset to the chain + * @preset: codec preset table to add + */ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); @@ -86,6 +90,10 @@ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) } EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset); +/** + * snd_hda_delete_codec_preset - Delete a codec preset from the chain + * @preset: codec preset table to delete + */ int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); @@ -1187,7 +1195,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg); -/* remember the current pinctl target value */ +/** + * snd_hda_codec_set_pin_target - remember the current pinctl target value + * @codec: the HDA codec + * @nid: pin NID + * @val: assigned pinctl value + * + * This function stores the given value to a pinctl target value in the + * pincfg table. This isn't always as same as the actually written value + * but can be referred at any time via snd_hda_codec_get_pin_target(). + */ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, unsigned int val) { @@ -1201,7 +1218,11 @@ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target); -/* return the current pinctl target value */ +/** + * snd_hda_codec_get_pin_target - return the current pinctl target value + * @codec: the HDA codec + * @nid: pin NID + */ int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; @@ -1573,6 +1594,13 @@ int snd_hda_codec_new(struct hda_bus *bus, } EXPORT_SYMBOL_GPL(snd_hda_codec_new); +/** + * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults + * @codec: the HDA codec + * + * Forcibly refresh the all widget caps and the init pin configurations of + * the given codec. + */ int snd_hda_codec_update_widgets(struct hda_codec *codec) { hda_nid_t fg; @@ -2239,7 +2267,17 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); -/* Works like snd_hda_codec_amp_update() but it writes the value only at +/** + * snd_hda_codec_amp_init - initialize the AMP value + * @codec: the HDA codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Works like snd_hda_codec_amp_update() but it writes the value only at * the first access. If the amp was already initialized / updated beforehand, * this does nothing. */ @@ -2250,6 +2288,17 @@ int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); +/** + * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value + * @codec: the HDA codec + * @nid: NID to read the AMP value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Call snd_hda_codec_amp_init() for both stereo channels. + */ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val) { @@ -2644,7 +2693,10 @@ void snd_hda_ctls_clear(struct hda_codec *codec) snd_array_free(&codec->nids); } -/* pseudo device locking +/** + * snd_hda_lock_devices - pseudo device locking + * @bus: the BUS + * * toggle card->shutdown to allow/disallow the device access (as a hack) */ int snd_hda_lock_devices(struct hda_bus *bus) @@ -2681,6 +2733,10 @@ int snd_hda_lock_devices(struct hda_bus *bus) } EXPORT_SYMBOL_GPL(snd_hda_lock_devices); +/** + * snd_hda_unlock_devices - pseudo device unlocking + * @bus: the BUS + */ void snd_hda_unlock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; @@ -2867,7 +2923,7 @@ static int add_slave(struct hda_codec *codec, } /** - * snd_hda_add_vmaster - create a virtual master control and add slaves + * __snd_hda_add_vmaster - create a virtual master control and add slaves * @codec: HD-audio codec * @name: vmaster control name * @tlv: TLV data (optional) @@ -2970,10 +3026,15 @@ static struct snd_kcontrol_new vmaster_mute_mode = { .put = vmaster_mute_mode_put, }; -/* - * Add a mute-LED hook with the given vmaster switch kctl - * "Mute-LED Mode" control is automatically created and associated with - * the given hook. +/** + * snd_hda_add_vmaster_hook - Add a vmaster hook for mute-LED + * @codec: the HDA codec + * @hook: the vmaster hook object + * @expose_enum_ctl: flag to create an enum ctl + * + * Add a mute-LED hook with the given vmaster switch kctl. + * When @expose_enum_ctl is set, "Mute-LED Mode" control is automatically + * created and associated with the given hook. */ int snd_hda_add_vmaster_hook(struct hda_codec *codec, struct hda_vmaster_mute_hook *hook, @@ -2995,9 +3056,12 @@ int snd_hda_add_vmaster_hook(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook); -/* - * Call the hook with the current value for synchronization - * Should be called in init callback +/** + * snd_hda_sync_vmaster_hook - Sync vmaster hook + * @hook: the vmaster hook + * + * Call the hook with the current value for synchronization. + * Should be called in init callback. */ void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) { @@ -3599,7 +3663,11 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls); -/* get the hda_spdif_out entry from the given NID +/** + * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID + * @codec: the HDA codec + * @nid: widget NID + * * call within spdif_mutex lock */ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, @@ -3616,6 +3684,13 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid); +/** + * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl + * @codec: the HDA codec + * @idx: the SPDIF ctl index + * + * Unassign the widget from the given SPDIF control. + */ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) { struct hda_spdif_out *spdif; @@ -3627,6 +3702,14 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) } EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign); +/** + * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID + * @codec: the HDA codec + * @idx: the SPDIF ctl idx + * @nid: widget NID + * + * Assign the widget to the SPDIF control with the given index. + */ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) { struct hda_spdif_out *spdif; @@ -3946,6 +4029,16 @@ void snd_hda_codec_flush_cache(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); +/** + * snd_hda_codec_set_power_to_all - Set the power state to all widgets + * @codec: the HDA codec + * @fg: function group (not used now) + * @power_state: the power state to set (AC_PWRST_*) + * + * Set the given power state to all widgets that have the power control. + * If the codec has power_filter set, it evaluates the power state and + * filter out if it's unchanged as D3. + */ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { @@ -4010,7 +4103,15 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, return state; } -/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */ +/** + * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD + * @codec: the HDA codec + * @nid: widget NID + * @power_state: power state to evalue + * + * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set. + * This can be used a codec power_filter callback. + */ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) @@ -4671,6 +4772,17 @@ static int set_pcm_default_values(struct hda_codec *codec, /* * codec prepare/cleanup entries */ +/** + * snd_hda_codec_prepare - Prepare a stream + * @codec: the HDA codec + * @hinfo: PCM information + * @stream: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign + * + * Calls the prepare callback set by the codec with the given arguments. + * Clean up the inactive streams when successful. + */ int snd_hda_codec_prepare(struct hda_codec *codec, struct hda_pcm_stream *hinfo, unsigned int stream, @@ -4687,6 +4799,14 @@ int snd_hda_codec_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_codec_prepare); +/** + * snd_hda_codec_cleanup - Prepare a stream + * @codec: the HDA codec + * @hinfo: PCM information + * @substream: PCM substream + * + * Calls the cleanup callback set by the codec with the given arguments. + */ void snd_hda_codec_cleanup(struct hda_codec *codec, struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index b2d81ab..e664307 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -20,6 +20,16 @@ #include "hda_auto_parser.h" #include "hda_jack.h" +/** + * is_jack_detectable - Check whether the given pin is jack-detectable + * @codec: the HDA codec + * @nid: pin NID + * + * Check whether the given pin is capable to report the jack detection. + * The jack detection might not work by various reasons, e.g. the jack + * detection is prohibited in the codec level, the pin config has + * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc. + */ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) { if (codec->no_jack_detect) @@ -268,6 +278,14 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback); +/** + * snd_hda_jack_detect_enable - Enable the jack detection on the given pin + * @codec: the HDA codec + * @nid: pin NID to enable jack detection + * + * Enable the jack detection with the default callback. Returns zero if + * successful or a negative error code. + */ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid) { return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL)); @@ -410,6 +428,15 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; } +/** + * snd_hda_jack_add_kctl - Add a jack kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID + * @name: the name string for the jack ctl + * @idx: the ctl index for the jack ctl + * + * This is a simple helper calling __snd_hda_jack_add_kctl(). + */ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, const char *name, int idx) { @@ -552,6 +579,11 @@ static void call_jack_callback(struct hda_codec *codec, } } +/** + * snd_hda_jack_unsol_event - Handle an unsolicited event + * @codec: the HDA codec + * @res: the unsolicited event data + */ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; @@ -567,6 +599,13 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) } EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); +/** + * snd_hda_jack_poll_all - Poll all jacks + * @codec: the HDA codec + * + * Poll all detectable jacks with dirty flag, update the status, call + * callbacks and call snd_hda_jack_report_sync() if any changes are found. + */ void snd_hda_jack_poll_all(struct hda_codec *codec) { struct hda_jack_tbl *jack = codec->jacktbl.list; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 13cb375..b279e32 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -72,6 +72,11 @@ enum { int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid); +/** + * snd_hda_jack_detect - Detect the jack + * @codec: the HDA codec + * @nid: pin NID to check jack detection + */ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 9b49f15..bef7215 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -417,8 +417,13 @@ static DEVICE_ATTR_RW(user_pin_configs); static DEVICE_ATTR_WO(reconfig); static DEVICE_ATTR_WO(clear); -/* - * Look for hint string +/** + * snd_hda_get_hint - Look for hint string + * @codec: the HDA codec + * @key: the hint key string + * + * Look for a hint key/value pair matching with the given key string + * and returns the value string. If nothing found, returns NULL. */ const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) { @@ -427,6 +432,15 @@ const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) } EXPORT_SYMBOL_GPL(snd_hda_get_hint); +/** + * snd_hda_get_bool_hint - Get a boolean hint value + * @codec: the HDA codec + * @key: the hint key string + * + * Look for a hint key/value pair matching with the given key string + * and returns a boolean value parsed from the value. If no matching + * key is found, return a negative value. + */ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { const char *p; @@ -453,6 +467,16 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) } EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint); +/** + * snd_hda_get_bool_hint - Get a boolean hint value + * @codec: the HDA codec + * @key: the hint key string + * @valp: pointer to store a value + * + * Look for a hint key/value pair matching with the given key string + * and stores the integer value to @valp. If no matching key is found, + * return a negative error code. Otherwise it returns zero. + */ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) { const char *p; @@ -690,8 +714,11 @@ static int get_line_from_fw(char *buf, int size, size_t *fw_size_p, return 1; } -/* - * load a "patch" firmware file and parse it +/** + * snd_hda_load_patch - load a "patch" firmware file and parse it + * @bus: HD-audio bus + * @fw_size: the firmware byte size + * @fw_buf: the firmware data */ int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf) { -- cgit v0.10.2 From df57de172a47f16548ee4bb69d1110e32686d6a9 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 29 Oct 2014 20:09:45 +0530 Subject: ALSA: hdspm: remove unused variable removed the unused variables. These variables were only being assigned some value, but the values were never being used. it has been build tested after removing the variables. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 7f7277b..e09348c1 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1257,14 +1257,13 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate) /* check for external sample rate, returns the sample rate in Hz*/ static int hdspm_external_sample_rate(struct hdspm *hdspm) { - unsigned int status, status2, timecode; + unsigned int status, status2; int syncref, rate = 0, rate_bits; switch (hdspm->io_type) { case AES32: status2 = hdspm_read(hdspm, HDSPM_statusRegister2); status = hdspm_read(hdspm, HDSPM_statusRegister); - timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); syncref = hdspm_autosync_ref(hdspm); switch (syncref) { @@ -4862,18 +4861,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status, status2, control, freq; + unsigned int status, status2; char *pref_sync_ref; char *autosync_ref; char *system_clock_mode; - char *insel; int x, x2; status = hdspm_read(hdspm, HDSPM_statusRegister); status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - control = hdspm->control_register; - freq = hdspm_read(hdspm, HDSPM_timecodeRegister); snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", hdspm->card_name, hdspm->card->number + 1, @@ -4936,17 +4932,6 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, snd_iprintf(buffer, "Line out: %s\n", (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); - switch (hdspm->control_register & HDSPM_InputMask) { - case HDSPM_InputOptical: - insel = "Optical"; - break; - case HDSPM_InputCoaxial: - insel = "Coaxial"; - break; - default: - insel = "Unknown"; - } - snd_iprintf(buffer, "ClearTrackMarker = %s, Transmit in %s Channel Mode, " "Auto Input %s\n", @@ -5191,15 +5176,13 @@ snd_hdspm_proc_read_raydat(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status1, status2, status3, control, i; + unsigned int status1, status2, status3, i; unsigned int lock, sync; status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */ status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */ status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */ - control = hdspm->control_register; - snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1); snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2); snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3); -- cgit v0.10.2 From eafe8404c103b3051b6421fc17e0e8b91d369f0b Mon Sep 17 00:00:00 2001 From: Tina Ruchandani Date: Wed, 29 Oct 2014 10:48:10 -0700 Subject: ALSA: es1968: Replace timeval with ktime_t es1968_measure_clock uses struct timeval, which on 32-bit systems will overflow in 2038, leading to incorrect interpretation of time.This patch changes the function to use ktime_t instead of struct timeval, which implies: - no y2038: ktime_t uses a 64-bit datatype explicitly. - efficent subtraction: The earlier version computes the difference in usecs while dealing with secs and nsecs. It requires checks to see if the nsecs of stop is less than start. This patch uses a direct subtract of ktime_t and converts to usecs. - use of monotonic clock (ktime_get) over real time (do_gettimeofday), which simplifies timekeeping, as it does not have to deal with cases where stop_time is less than start_time. Signed-off-by: Tina Ruchandani Reviewed-by: Arnd Bergmann Signed-off-by: Takashi Iwai diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index a9956a7..6039700 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1710,7 +1710,8 @@ static void es1968_measure_clock(struct es1968 *chip) int i, apu; unsigned int pa, offset, t; struct esm_memory *memory; - struct timeval start_time, stop_time; + ktime_t start_time, stop_time; + ktime_t diff; if (chip->clock == 0) chip->clock = 48000; /* default clock value */ @@ -1761,12 +1762,12 @@ static void es1968_measure_clock(struct es1968 *chip) snd_es1968_bob_inc(chip, ESM_BOB_FREQ); __apu_set_register(chip, apu, 5, pa & 0xffff); snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); - do_gettimeofday(&start_time); + start_time = ktime_get(); spin_unlock_irq(&chip->reg_lock); msleep(50); spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); - do_gettimeofday(&stop_time); + stop_time = ktime_get(); snd_es1968_trigger_apu(chip, apu, 0); /* stop */ snd_es1968_bob_dec(chip); chip->in_measurement = 0; @@ -1777,12 +1778,8 @@ static void es1968_measure_clock(struct es1968 *chip) offset &= 0xfffe; offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2); - t = stop_time.tv_sec - start_time.tv_sec; - t *= 1000000; - if (stop_time.tv_usec < start_time.tv_usec) - t -= start_time.tv_usec - stop_time.tv_usec; - else - t += stop_time.tv_usec - start_time.tv_usec; + diff = ktime_sub(stop_time, start_time); + t = ktime_to_us(diff); if (t == 0) { dev_err(chip->card->dev, "?? calculation error..\n"); } else { -- cgit v0.10.2 From dda42bd0c3a4b7be1561546914eda59b68a58be4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 12:04:50 +0100 Subject: ALSA: hda - Add kerneldoc comments to hda_generic.c Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 06d7210..63b69f7 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -40,7 +40,12 @@ #include "hda_generic.h" -/* initialize hda_gen_spec struct */ +/** + * snd_hda_gen_spec_init - initialize hda_gen_spec struct + * @spec: hda_gen_spec object to initialize + * + * Initialize the given hda_gen_spec object. + */ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); @@ -51,6 +56,17 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) } EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); +/** + * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template + * @spec: hda_gen_spec object + * @name: name string to override the template, NULL if unchanged + * @temp: template for the new kctl + * + * Add a new kctl (actually snd_kcontrol_new to be instantiated later) + * element based on the given snd_kcontrol_new template @temp and the + * name string @name to the list in @spec. + * Returns the newly created object or NULL as error. + */ struct snd_kcontrol_new * snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, const struct snd_kcontrol_new *temp) @@ -259,8 +275,14 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, return NULL; } -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard +/** + * snd_hda_get_nid_path - get the path between the given NIDs + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * + * Return the found nid_path object or NULL for error. + * Passing 0 to either @from_nid or @to_nid behaves as a wildcard. */ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) @@ -269,8 +291,14 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_get_nid_path); -/* get the index number corresponding to the path instance; - * the index starts from 1, for easier checking the invalid value +/** + * snd_hda_get_path_idx - get the index number corresponding to the path + * instance + * @codec: the HDA codec + * @path: nid_path object + * + * The returned index starts from 1, i.e. the actual array index with offset 1, + * and zero is handled as an invalid path */ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) { @@ -287,7 +315,12 @@ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) } EXPORT_SYMBOL_GPL(snd_hda_get_path_idx); -/* get the path instance corresponding to the given index number */ +/** + * snd_hda_get_path_from_idx - get the path instance corresponding to the + * given index number + * @codec: the HDA codec + * @idx: the path index + */ struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) { struct hda_gen_spec *spec = codec->spec; @@ -415,7 +448,18 @@ static bool __parse_nid_path(struct hda_codec *codec, return true; } -/* parse the widget path from the given nid to the target nid; +/** + * snd_hda_parse_nid_path - parse the widget path from the given nid to + * the target nid + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * @anchor_nid: the anchor indication + * @path: the path object to store the result + * + * Returns true if a matching path is found. + * + * The parsing behavior depends on parameters: * when @from_nid is 0, try to find an empty DAC; * when @anchor_nid is set to a positive value, only paths through the widget * with the given value are evaluated. @@ -436,9 +480,15 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, } EXPORT_SYMBOL_GPL(snd_hda_parse_nid_path); -/* - * parse the path between the given NIDs and add to the path list. - * if no valid path is found, return NULL +/** + * snd_hda_add_new_path - parse the path between the given NIDs and + * add to the path list + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() + * + * If no valid path is found, returns NULL. */ struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, @@ -724,8 +774,14 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, } } -/* activate or deactivate the given path - * if @add_aamix is set, enable the input from aa-mix NID as well (if any) +/** + * snd_hda_activate_path - activate or deactivate the given path + * @codec: the HDA codec + * @path: the path to activate/deactivate + * @enable: flag to activate or not + * @add_aamix: enable the input from aamix NID + * + * If @add_aamix is set, enable the input from aa-mix NID as well (if any). */ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix) @@ -3883,7 +3939,12 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } } -/* Toggle outputs muting */ +/** + * snd_hda_gen_update_outputs - Toggle outputs muting + * @codec: the HDA codec + * + * Update the mute status of all outputs based on the current jack states. + */ void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3944,7 +4005,11 @@ static void call_update_outputs(struct hda_codec *codec) snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); } -/* standard HP-automute helper */ +/** + * snd_hda_gen_hp_automute - standard HP-automute helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -3965,7 +4030,11 @@ void snd_hda_gen_hp_automute(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); -/* standard line-out-automute helper */ +/** + * snd_hda_gen_line_automute - standard line-out-automute helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -3986,7 +4055,11 @@ void snd_hda_gen_line_automute(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); -/* standard mic auto-switch helper */ +/** + * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -4318,7 +4391,13 @@ static int check_auto_mic_availability(struct hda_codec *codec) return 0; } -/* power_filter hook; make inactive widgets into power down */ +/** + * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets + * into power down + * @codec: the HDA codec + * @nid: NID to evalute + * @power_state: target power state + */ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) @@ -4354,8 +4433,11 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) } } -/* - * Parse the given BIOS configuration and set up the hda_gen_spec +/** + * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and + * set up the hda_gen_spec + * @codec: the HDA codec + * @cfg: Parsed pin configuration * * return 1 if successful, 0 if the proper config is not found, * or a negative error code @@ -4541,6 +4623,12 @@ static const char * const slave_pfxs[] = { NULL, }; +/** + * snd_hda_gen_build_controls - Build controls from the parsed results + * @codec: the HDA codec + * + * Pass this to build_controls patch_ops. + */ int snd_hda_gen_build_controls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -5018,7 +5106,12 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, strlcat(str, sfx, len); } -/* build PCM streams based on the parsed results */ +/** + * snd_hda_gen_build_pcms - build PCM streams based on the parsed results + * @codec: the HDA codec + * + * Pass this to build_pcms patch_ops. + */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -5313,9 +5406,11 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) } } -/* - * initialize the generic spec; - * this can be put as patch_ops.init function +/** + * snd_hda_gen_init - initialize the generic spec + * @codec: the HDA codec + * + * This can be put as patch_ops init function. */ int snd_hda_gen_init(struct hda_codec *codec) { @@ -5351,9 +5446,11 @@ int snd_hda_gen_init(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_gen_init); -/* - * free the generic spec; - * this can be put as patch_ops.free function +/** + * snd_hda_gen_free - free the generic spec + * @codec: the HDA codec + * + * This can be put as patch_ops free function. */ void snd_hda_gen_free(struct hda_codec *codec) { @@ -5365,9 +5462,12 @@ void snd_hda_gen_free(struct hda_codec *codec) EXPORT_SYMBOL_GPL(snd_hda_gen_free); #ifdef CONFIG_PM -/* - * check the loopback power save state; - * this can be put as patch_ops.check_power_status function +/** + * snd_hda_gen_check_power_status - check the loopback power save state + * @codec: the HDA codec + * @nid: NID to inspect + * + * This can be put as patch_ops check_power_status function. */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -5393,6 +5493,12 @@ static const struct hda_codec_ops generic_patch_ops = { #endif }; +/** + * snd_hda_parse_generic_codec - Generic codec parser + * @codec: the HDA codec + * + * This should be called from the HDA codec core. + */ int snd_hda_parse_generic_codec(struct hda_codec *codec) { struct hda_gen_spec *spec; -- cgit v0.10.2 From 438f4e2801d730442d6fc36ab2b265c4b54cff8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 12:44:00 +0100 Subject: ALSA: doc: Add ASoC codes into API documentation Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl index 7bec270..b2d2c3b 100644 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -104,13 +104,30 @@ !Iinclude/sound/compress_driver.h + ASoC + ASoC Core API +!Iinclude/sound/soc.h +!Esound/soc/soc-core.c +!Esound/soc/soc-cache.c +!Esound/soc/soc-devres.c +!Esound/soc/soc-io.c +!Esound/soc/soc-pcm.c + + ASoC DAPM API +!Esound/soc/soc-dapm.c + + ASoC DMA Engine API +!Esound/soc/soc-generic-dmaengine-pcm.c + + Miscellaneous Functions Hardware-Dependent Devices API !Esound/core/hwdep.c Jack Abstraction Layer API -!Esound/core/jack.c !Iinclude/sound/jack.h +!Esound/core/jack.c +!Esound/soc/soc-jack.c ISA DMA Helpers !Esound/core/isadma.c -- cgit v0.10.2 From 12cddbd8696657ff405e054be48747b906731698 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 13:44:34 +0100 Subject: ALSA: control: Add missing kerneldoc comments to exported functions A few functions have no proper documentation yet, so let's add them. Along with it, remove superfluous blank line between the closing brace and EXPORT_SYMBOL() line. Signed-off-by: Takashi Iwai diff --git a/sound/core/control.c b/sound/core/control.c index 5c35bba..99aa3aa 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -141,6 +141,16 @@ static int snd_ctl_release(struct inode *inode, struct file *file) return 0; } +/** + * snd_ctl_notify - Send notification to user-space for a control change + * @card: the card to send notification + * @mask: the event mask, SNDRV_CTL_EVENT_* + * @id: the ctl element id to send notification + * + * This function adds an event record with the given id and mask, appends + * to the list and wakes up the user-space for notification. This can be + * called in the atomic context. + */ void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id) { @@ -179,7 +189,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, } read_unlock(&card->ctl_files_rwlock); } - EXPORT_SYMBOL(snd_ctl_notify); /** @@ -261,7 +270,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.private_data = private_data; return snd_ctl_new(&kctl, access); } - EXPORT_SYMBOL(snd_ctl_new1); /** @@ -280,7 +288,6 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol) kfree(kcontrol); } } - EXPORT_SYMBOL(snd_ctl_free_one); static bool snd_ctl_remove_numid_conflict(struct snd_card *card, @@ -376,7 +383,6 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return err; } - EXPORT_SYMBOL(snd_ctl_add); /** @@ -471,7 +477,6 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return 0; } - EXPORT_SYMBOL(snd_ctl_remove); /** @@ -499,7 +504,6 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) up_write(&card->controls_rwsem); return ret; } - EXPORT_SYMBOL(snd_ctl_remove_id); /** @@ -617,7 +621,6 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, up_write(&card->controls_rwsem); return 0; } - EXPORT_SYMBOL(snd_ctl_rename_id); /** @@ -645,7 +648,6 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi } return NULL; } - EXPORT_SYMBOL(snd_ctl_find_numid); /** @@ -687,7 +689,6 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, } return NULL; } - EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, @@ -1526,19 +1527,28 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head * return 0; } +/** + * snd_ctl_register_ioctl - register the device-specific control-ioctls + * @fcn: ioctl callback function + * + * called from each device manager like pcm.c, hwdep.c, etc. + */ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls); } - EXPORT_SYMBOL(snd_ctl_register_ioctl); #ifdef CONFIG_COMPAT +/** + * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat + * control-ioctls + * @fcn: ioctl callback function + */ int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls); } - EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); #endif @@ -1566,19 +1576,26 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, return -EINVAL; } +/** + * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls + * @fcn: ioctl callback function to unregister + */ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls); } - EXPORT_SYMBOL(snd_ctl_unregister_ioctl); #ifdef CONFIG_COMPAT +/** + * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit + * control-ioctls + * @fcn: ioctl callback function to unregister + */ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls); } - EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); #endif @@ -1702,6 +1719,16 @@ int snd_ctl_create(struct snd_card *card) /* * Frequently used control callbacks/helpers */ + +/** + * snd_ctl_boolean_mono_info - Helper function for a standard boolean info + * callback with a mono channel + * @kcontrol: the kcontrol instance + * @uinfo: info to store + * + * This is a function that can be used as info callback for a standard + * boolean control with a single mono channel. + */ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1711,9 +1738,17 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 1; return 0; } - EXPORT_SYMBOL(snd_ctl_boolean_mono_info); +/** + * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info + * callback with stereo two channels + * @kcontrol: the kcontrol instance + * @uinfo: info to store + * + * This is a function that can be used as info callback for a standard + * boolean control with stereo two channels. + */ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1723,7 +1758,6 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 1; return 0; } - EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); /** -- cgit v0.10.2 From f213d8f79a3928eaa2e2936f8ab40761aac04b95 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 13:45:58 +0100 Subject: ALSA: pcm: Use static inline for snd_pcm_lib_alloc_vmalloc_buffer() ... instead of #if 0 hack. It's more straightforward and obvious. Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e862497..5c3310d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -942,7 +942,6 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream); struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, unsigned long offset); -#if 0 /* for kernel-doc */ /** * snd_pcm_lib_alloc_vmalloc_buffer - allocate virtual DMA buffer * @substream: the substream to allocate the buffer to @@ -955,8 +954,13 @@ struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, * Return: 1 if the buffer was changed, 0 if not changed, or a negative error * code. */ -static int snd_pcm_lib_alloc_vmalloc_buffer - (struct snd_pcm_substream *substream, size_t size); +static inline int snd_pcm_lib_alloc_vmalloc_buffer + (struct snd_pcm_substream *substream, size_t size) +{ + return _snd_pcm_lib_alloc_vmalloc_buffer(substream, size, + GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); +} + /** * snd_pcm_lib_alloc_vmalloc_32_buffer - allocate 32-bit-addressable buffer * @substream: the substream to allocate the buffer to @@ -968,15 +972,12 @@ static int snd_pcm_lib_alloc_vmalloc_buffer * Return: 1 if the buffer was changed, 0 if not changed, or a negative error * code. */ -static int snd_pcm_lib_alloc_vmalloc_32_buffer - (struct snd_pcm_substream *substream, size_t size); -#endif -#define snd_pcm_lib_alloc_vmalloc_buffer(subs, size) \ - _snd_pcm_lib_alloc_vmalloc_buffer \ - (subs, size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO) -#define snd_pcm_lib_alloc_vmalloc_32_buffer(subs, size) \ - _snd_pcm_lib_alloc_vmalloc_buffer \ - (subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO) +static inline int snd_pcm_lib_alloc_vmalloc_32_buffer + (struct snd_pcm_substream *substream, size_t size) +{ + return _snd_pcm_lib_alloc_vmalloc_buffer(substream, size, + GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); +} #define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p) -- cgit v0.10.2 From 30b771cf8c3120c5c946811ecc5a9b87a34003a2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 15:02:50 +0100 Subject: ALSA: pcm: More kerneldoc updates Add proper kerneldoc comments to the exported functions. Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 5c3310d..f78a572 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -533,6 +533,12 @@ snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size) * PCM library */ +/** + * snd_pcm_stream_linked - Check whether the substream is linked with others + * @substream: substream to check + * + * Returns true if the given substream is being linked with others. + */ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream) { return substream->group != &substream->self_group; @@ -543,6 +549,16 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream); void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream); void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream); unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream); + +/** + * snd_pcm_stream_lock_irqsave - Lock the PCM stream + * @substream: PCM substream + * @flags: irq flags + * + * This locks the PCM stream like snd_pcm_stream_lock() but with the local + * IRQ (only when nonatomic is false). In nonatomic case, this is identical + * as snd_pcm_stream_lock(). + */ #define snd_pcm_stream_lock_irqsave(substream, flags) \ do { \ typecheck(unsigned long, flags); \ @@ -551,9 +567,25 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream); void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, unsigned long flags); +/** + * snd_pcm_group_for_each_entry - iterate over the linked substreams + * @s: the iterator + * @substream: the substream + * + * Iterate over the all linked substreams to the given @substream. + * When @substream isn't linked with any others, this gives returns @substream + * itself once. + */ #define snd_pcm_group_for_each_entry(s, substream) \ list_for_each_entry(s, &substream->group->substreams, link_list) +/** + * snd_pcm_running - Check whether the substream is in a running state + * @substream: substream to check + * + * Returns true if the given substream is in the state RUNNING, or in the + * state DRAINING for playback. + */ static inline int snd_pcm_running(struct snd_pcm_substream *substream) { return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || @@ -561,45 +593,81 @@ static inline int snd_pcm_running(struct snd_pcm_substream *substream) substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); } +/** + * bytes_to_samples - Unit conversion of the size from bytes to samples + * @runtime: PCM runtime instance + * @size: size in bytes + */ static inline ssize_t bytes_to_samples(struct snd_pcm_runtime *runtime, ssize_t size) { return size * 8 / runtime->sample_bits; } +/** + * bytes_to_frames - Unit conversion of the size from bytes to frames + * @runtime: PCM runtime instance + * @size: size in bytes + */ static inline snd_pcm_sframes_t bytes_to_frames(struct snd_pcm_runtime *runtime, ssize_t size) { return size * 8 / runtime->frame_bits; } +/** + * samples_to_bytes - Unit conversion of the size from samples to bytes + * @runtime: PCM runtime instance + * @size: size in samples + */ static inline ssize_t samples_to_bytes(struct snd_pcm_runtime *runtime, ssize_t size) { return size * runtime->sample_bits / 8; } +/** + * frames_to_bytes - Unit conversion of the size from frames to bytes + * @runtime: PCM runtime instance + * @size: size in frames + */ static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime, snd_pcm_sframes_t size) { return size * runtime->frame_bits / 8; } +/** + * frame_aligned - Check whether the byte size is aligned to frames + * @runtime: PCM runtime instance + * @bytes: size in bytes + */ static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes) { return bytes % runtime->byte_align == 0; } +/** + * snd_pcm_lib_buffer_bytes - Get the buffer size of the current PCM in bytes + * @substream: PCM substream + */ static inline size_t snd_pcm_lib_buffer_bytes(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; return frames_to_bytes(runtime, runtime->buffer_size); } +/** + * snd_pcm_lib_period_bytes - Get the period size of the current PCM in bytes + * @substream: PCM substream + */ static inline size_t snd_pcm_lib_period_bytes(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; return frames_to_bytes(runtime, runtime->period_size); } -/* - * result is: 0 ... (boundary - 1) +/** + * snd_pcm_playback_avail - Get the available (writable) space for playback + * @runtime: PCM runtime instance + * + * Result is between 0 ... (boundary - 1) */ static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime) { @@ -611,8 +679,11 @@ static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *r return avail; } -/* - * result is: 0 ... (boundary - 1) +/** + * snd_pcm_playback_avail - Get the available (readable) space for capture + * @runtime: PCM runtime instance + * + * Result is between 0 ... (boundary - 1) */ static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *runtime) { @@ -622,11 +693,19 @@ static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *ru return avail; } +/** + * snd_pcm_playback_hw_avail - Get the queued space for playback + * @runtime: PCM runtime instance + */ static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(struct snd_pcm_runtime *runtime) { return runtime->buffer_size - snd_pcm_playback_avail(runtime); } +/** + * snd_pcm_capture_hw_avail - Get the free space for capture + * @runtime: PCM runtime instance + */ static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime *runtime) { return runtime->buffer_size - snd_pcm_capture_avail(runtime); @@ -706,6 +785,20 @@ static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream) return snd_pcm_capture_avail(runtime) == 0; } +/** + * snd_pcm_trigger_done - Mark the master substream + * @substream: the pcm substream instance + * @master: the linked master substream + * + * When multiple substreams of the same card are linked and the hardware + * supports the single-shot operation, the driver calls this in the loop + * in snd_pcm_group_for_each_entry() for marking the substream as "done". + * Then most of trigger operations are performed only to the given master + * substream. + * + * The trigger_master mark is cleared at timestamp updates at the end + * of trigger operations. + */ static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream, struct snd_pcm_substream *master) { @@ -881,6 +974,14 @@ unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, unsigned int rates_b); +/** + * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer + * @substream: PCM substream to set + * @bufp: the buffer information, NULL to clear + * + * Copy the buffer information to runtime->dma_buffer when @bufp is non-NULL. + * Otherwise it clears the current buffer information. + */ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream, struct snd_dma_buffer *bufp) { @@ -906,6 +1007,11 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); void snd_pcm_timer_init(struct snd_pcm_substream *substream); void snd_pcm_timer_done(struct snd_pcm_substream *substream); +/** + * snd_pcm_gettime - Fill the timespec depending on the timestamp mode + * @runtime: PCM runtime instance + * @tv: timespec to fill + */ static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime, struct timespec *tv) { @@ -997,18 +1103,35 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, #define snd_pcm_sgbuf_ops_page NULL #endif /* SND_DMA_SGBUF */ +/** + * snd_pcm_sgbuf_get_addr - Get the DMA address at the corresponding offset + * @substream: PCM substream + * @ofs: byte offset + */ static inline dma_addr_t snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs) { return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs); } +/** + * snd_pcm_sgbuf_get_ptr - Get the virtual address at the corresponding offset + * @substream: PCM substream + * @ofs: byte offset + */ static inline void * snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs) { return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs); } +/** + * snd_pcm_sgbuf_chunk_size - Compute the max size that fits within the contig. + * page from the given size + * @substream: PCM substream + * @ofs: byte offset + * @size: byte size to examine + */ static inline unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, unsigned int ofs, unsigned int size) @@ -1016,13 +1139,24 @@ snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size); } -/* handle mmap counter - PCM mmap callback should handle this counter properly */ +/** + * snd_pcm_mmap_data_open - increase the mmap counter + * @area: VMA + * + * PCM mmap callback should handle this counter properly + */ static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) { struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; atomic_inc(&substream->mmap_count); } +/** + * snd_pcm_mmap_data_close - decrease the mmap counter + * @area: VMA + * + * PCM mmap callback should handle this counter properly + */ static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area) { struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; @@ -1042,6 +1176,11 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s #define snd_pcm_lib_mmap_vmalloc NULL +/** + * snd_pcm_limit_isa_dma_size - Get the max size fitting with ISA DMA transfer + * @dma: DMA number + * @max: pointer to store the max size + */ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { *max = dma < 4 ? 64 * 1024 : 128 * 1024; @@ -1094,7 +1233,11 @@ struct snd_pcm_chmap { void *private_data; /* optional: private data pointer */ }; -/* get the PCM substream assigned to the given chmap info */ +/** + * snd_pcm_chmap_substream - get the PCM substream assigned to the given chmap info + * @info: chmap information + * @idx: the substream number index + */ static inline struct snd_pcm_substream * snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx) { @@ -1121,7 +1264,10 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, unsigned long private_value, struct snd_pcm_chmap **info_ret); -/* Strong-typed conversion of pcm_format to bitwise */ +/** + * pcm_format_to_bits - Strong-typed conversion of pcm_format to bitwise + * @pcm_format: PCM format + */ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) { return 1ULL << (__force int) pcm_format; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 42ded99..31acc3d 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -218,6 +218,10 @@ static char *snd_pcm_format_names[] = { FORMAT(DSD_U32_LE), }; +/** + * snd_pcm_format_name - Return a name string for the given PCM format + * @format: PCM format + */ const char *snd_pcm_format_name(snd_pcm_format_t format) { if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) @@ -707,7 +711,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } return 0; } - EXPORT_SYMBOL(snd_pcm_new_stream); static int _snd_pcm_new(struct snd_card *card, const char *id, int device, @@ -1155,6 +1158,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) return 0; } +/** + * snd_pcm_notify - Add/remove the notify list + * @notify: PCM notify list + * @nfree: 0 = register, 1 = unregister + * + * This adds the given notifier to the global list so that the callback is + * called for each registered PCM devices. This exists only for PCM OSS + * emulation, so far. + */ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { struct snd_pcm *pcm; @@ -1177,7 +1189,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) mutex_unlock(®ister_mutex); return 0; } - EXPORT_SYMBOL(snd_pcm_notify); #ifdef CONFIG_PROC_FS diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 2f7ad10..4d5795d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -74,6 +74,14 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); static DEFINE_RWLOCK(snd_pcm_link_rwlock); static DECLARE_RWSEM(snd_pcm_link_rwsem); +/** + * snd_pcm_stream_lock - Lock the PCM stream + * @substream: PCM substream + * + * This locks the PCM stream's spinlock or mutex depending on the nonatomic + * flag of the given substream. This also takes the global link rw lock + * (or rw sem), too, for avoiding the race with linked streams. + */ void snd_pcm_stream_lock(struct snd_pcm_substream *substream) { if (substream->pcm->nonatomic) { @@ -86,6 +94,12 @@ void snd_pcm_stream_lock(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); +/** + * snd_pcm_stream_lock - Unlock the PCM stream + * @substream: PCM substream + * + * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock(). + */ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) { if (substream->pcm->nonatomic) { @@ -98,6 +112,14 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); +/** + * snd_pcm_stream_lock_irq - Lock the PCM stream + * @substream: PCM substream + * + * This locks the PCM stream like snd_pcm_stream_lock() and disables the local + * IRQ (only when nonatomic is false). In nonatomic case, this is identical + * as snd_pcm_stream_lock(). + */ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) { if (!substream->pcm->nonatomic) @@ -106,6 +128,12 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); +/** + * snd_pcm_stream_unlock_irq - Unlock the PCM stream + * @substream: PCM substream + * + * This is a counter-part of snd_pcm_stream_lock_irq(). + */ void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) { snd_pcm_stream_unlock(substream); @@ -124,6 +152,13 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); +/** + * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream + * @substream: PCM substream + * @flags: irq flags + * + * This is a counter-part of snd_pcm_stream_lock_irqsave(). + */ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, unsigned long flags) { @@ -3312,6 +3347,15 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { /* * mmap the DMA buffer on RAM */ + +/** + * snd_pcm_lib_default_mmap - Default PCM data mmap function + * @substream: PCM substream + * @area: VMA + * + * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL, + * this function is invoked implicitly. + */ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { @@ -3343,6 +3387,15 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap); * mmap the DMA buffer on I/O memory area */ #if SNDRV_PCM_INFO_MMAP_IOMEM +/** + * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem + * @substream: PCM substream + * @area: VMA + * + * When your hardware uses the iomapped pages as the hardware buffer and + * wants to mmap it, pass this function as mmap pcm_ops. Note that this + * is supposed to work only on limited architectures. + */ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area) { -- cgit v0.10.2 From 85926e0fe8b48fe6227614ddc3a1cb41c0a3c51a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 15:06:13 +0100 Subject: ALSA: pcm: Convert params_*() with static inline functions ... and add proper kerneldoc comments. There is no big reason to keep them as macros. Static inline functions are safer in general, and suitable for kerneldoc, too. Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index f78a572..29eb09e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -841,18 +841,59 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } -#define params_channels(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min) -#define params_rate(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_RATE)->min) -#define params_period_size(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min) -#define params_periods(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIODS)->min) -#define params_buffer_size(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min) -#define params_buffer_bytes(p) \ - (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min) +/** + * params_channels - Get the number of channels from the hw params + * @p: hw params + */ +static inline unsigned int params_channels(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_CHANNELS)->min; +} + +/** + * params_channels - Get the sample rate from the hw params + * @p: hw params + */ +static inline unsigned int params_rate(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_RATE)->min; +} + +/** + * params_channels - Get the period size (in frames) from the hw params + * @p: hw params + */ +static inline unsigned int params_period_size(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min; +} + +/** + * params_channels - Get the number of periods from the hw params + * @p: hw params + */ +static inline unsigned int params_periods(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_PERIODS)->min; +} + +/** + * params_channels - Get the buffer size (in frames) from the hw params + * @p: hw params + */ +static inline unsigned int params_buffer_size(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min; +} + +/** + * params_channels - Get the buffer size (in bytes) from the hw params + * @p: hw params + */ +static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p) +{ + return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min; +} int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -- cgit v0.10.2 From eb9c38d54c9cad72101dfe7fefe4a784dd67da86 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 15:19:43 +0100 Subject: ALSA: doc: More kerneldoc comments on core components Some functions missed the proper kerneldoc comments. Signed-off-by: Takashi Iwai diff --git a/sound/core/init.c b/sound/core/init.c index 7bdfd19..074875d 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -438,17 +438,6 @@ int snd_card_disconnect(struct snd_card *card) EXPORT_SYMBOL(snd_card_disconnect); -/** - * snd_card_free - frees given soundcard structure - * @card: soundcard structure - * - * This function releases the soundcard structure and the all assigned - * devices automatically. That is, you don't have to release the devices - * by yourself. - * - * Return: Zero. Frees all associated devices and frees the control - * interface associated to given soundcard. - */ static int snd_card_do_free(struct snd_card *card) { #if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -469,6 +458,15 @@ static int snd_card_do_free(struct snd_card *card) return 0; } +/** + * snd_card_free_when_closed - Disconnect the card, free it later eventually + * @card: soundcard structure + * + * Unlike snd_card_free(), this function doesn't try to release the card + * resource immediately, but tries to disconnect at first. When the card + * is still in use, the function returns before freeing the resources. + * The card resources will be freed when the refcount gets to zero. + */ int snd_card_free_when_closed(struct snd_card *card) { int ret = snd_card_disconnect(card); @@ -479,6 +477,19 @@ int snd_card_free_when_closed(struct snd_card *card) } EXPORT_SYMBOL(snd_card_free_when_closed); +/** + * snd_card_free - frees given soundcard structure + * @card: soundcard structure + * + * This function releases the soundcard structure and the all assigned + * devices automatically. That is, you don't have to release the devices + * by yourself. + * + * This function waits until the all resources are properly released. + * + * Return: Zero. Frees all associated devices and frees the control + * interface associated to given soundcard. + */ int snd_card_free(struct snd_card *card) { struct completion released; diff --git a/sound/core/sound.c b/sound/core/sound.c index 38ad1a0..f133306 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -355,8 +355,13 @@ int snd_unregister_device(int type, struct snd_card *card, int dev) EXPORT_SYMBOL(snd_unregister_device); -/* get the assigned device to the given type and device number; - * the caller needs to release it via put_device() after using it +/** + * snd_get_device - get the assigned device to the given type and device number + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card:the card instance + * @dev: the device index + * + * The caller needs to release it via put_device() after using it. */ struct device *snd_get_device(int type, struct snd_card *card, int dev) { -- cgit v0.10.2 From 67faa6ebd7fc0a811f6c1f3e3c113571953489ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Oct 2014 15:37:37 +0100 Subject: ALSA: doc: Fix missing "I" for kerneldoc inclusion Fixes: 90446d0746c3 ('ALSA: doc: Add missing headers and compress stuff to alsa-driver-api.tmpl') Signed-off-by: Takashi Iwai diff --git a/Documentation/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl index b2d2c3b..71f9246 100644 --- a/Documentation/DocBook/alsa-driver-api.tmpl +++ b/Documentation/DocBook/alsa-driver-api.tmpl @@ -57,7 +57,7 @@ !Esound/core/pcm.c !Esound/core/pcm_lib.c !Esound/core/pcm_native.c -!include/sound/pcm.h +!Iinclude/sound/pcm.h PCM Format Helpers !Esound/core/pcm_misc.c -- cgit v0.10.2 From e3a4bd5eec52912108e287146052f2624acbec7a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 14:45:04 +0100 Subject: ALSA: pcm: Simplify snd_pcm_action_lock_irq() The function snd_pcm_action_lock_irq() can be much simplified by simply wrapping snd_pcm_action() with the stream lock. This was rather the original idea, but later it was open coded for optimization. However, looking at the optimization part closely, one notices that the probability of the optimized path is quite low; in normal situations, the linked stream action happens only for the triggered substream, thus the operation becomes identical. So the code simplification has a clear win, especially because we have now doubly codes for both atomic and non-atomic locks. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4d5795d..b92b605 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -947,28 +947,6 @@ static int snd_pcm_action(struct action_ops *ops, return res; } -static int snd_pcm_action_lock_mutex(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) -{ - int res; - - down_read(&snd_pcm_link_rwsem); - if (snd_pcm_stream_linked(substream)) { - mutex_lock(&substream->group->mutex); - mutex_lock(&substream->self_group.mutex); - res = snd_pcm_action_group(ops, substream, state, 1); - mutex_unlock(&substream->self_group.mutex); - mutex_unlock(&substream->group->mutex); - } else { - mutex_lock(&substream->self_group.mutex); - res = snd_pcm_action_single(ops, substream, state); - mutex_unlock(&substream->self_group.mutex); - } - up_read(&snd_pcm_link_rwsem); - return res; -} - /* * Note: don't use any locks before */ @@ -978,22 +956,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops, { int res; - if (substream->pcm->nonatomic) - return snd_pcm_action_lock_mutex(ops, substream, state); - - read_lock_irq(&snd_pcm_link_rwlock); - if (snd_pcm_stream_linked(substream)) { - spin_lock(&substream->group->lock); - spin_lock(&substream->self_group.lock); - res = snd_pcm_action_group(ops, substream, state, 1); - spin_unlock(&substream->self_group.lock); - spin_unlock(&substream->group->lock); - } else { - spin_lock(&substream->self_group.lock); - res = snd_pcm_action_single(ops, substream, state); - spin_unlock(&substream->self_group.lock); - } - read_unlock_irq(&snd_pcm_link_rwlock); + snd_pcm_stream_lock_irq(substream); + res = snd_pcm_action(ops, substream, state); + snd_pcm_stream_unlock_irq(substream); return res; } -- cgit v0.10.2 From aa8edd8ca6a110349b00e360823c64e8cd106289 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 15:19:36 +0100 Subject: ALSA: pcm: Refactoring snd_pcm_action() Just a small code refactoring to reduce more lines. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b92b605..ca224fa 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -900,14 +900,19 @@ static int snd_pcm_action_single(struct action_ops *ops, return res; } -/* call in mutex-protected context */ -static int snd_pcm_action_mutex(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) +/* + * Note: call with stream lock + */ +static int snd_pcm_action(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) { int res; - if (snd_pcm_stream_linked(substream)) { + if (!snd_pcm_stream_linked(substream)) + return snd_pcm_action_single(ops, substream, state); + + if (substream->pcm->nonatomic) { if (!mutex_trylock(&substream->group->mutex)) { mutex_unlock(&substream->self_group.mutex); mutex_lock(&substream->group->mutex); @@ -916,24 +921,6 @@ static int snd_pcm_action_mutex(struct action_ops *ops, res = snd_pcm_action_group(ops, substream, state, 1); mutex_unlock(&substream->group->mutex); } else { - res = snd_pcm_action_single(ops, substream, state); - } - return res; -} - -/* - * Note: call with stream lock - */ -static int snd_pcm_action(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) -{ - int res; - - if (substream->pcm->nonatomic) - return snd_pcm_action_mutex(ops, substream, state); - - if (snd_pcm_stream_linked(substream)) { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); spin_lock(&substream->group->lock); @@ -941,8 +928,6 @@ static int snd_pcm_action(struct action_ops *ops, } res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->group->lock); - } else { - res = snd_pcm_action_single(ops, substream, state); } return res; } -- cgit v0.10.2 From f57915cfa5b2b14c1cffa2e83c034f55e3f0e70d Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 22 Oct 2014 14:55:49 -0700 Subject: ib_isert: Add max_send_sge=2 minimum for control PDU responses This patch adds a max_send_sge=2 minimum in isert_conn_setup_qp() to ensure outgoing control PDU responses with tx_desc->num_sge=2 are able to function correctly. This addresses a bug with RDMA hardware using dev_attr.max_sge=3, that in the original code with the ConnectX-2 work-around would result in isert_conn->max_sge=1 being negotiated. Originally reported by Chris with ocrdma driver. Reported-by: Chris Moore Tested-by: Chris Moore Signed-off-by: Or Gerlitz Cc: # 3.10+ Signed-off-by: Nicholas Bellinger diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 3effa93..ef5b22b 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -115,9 +115,12 @@ isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id, attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS; /* * FIXME: Use devattr.max_sge - 2 for max_send_sge as - * work-around for RDMA_READ.. + * work-around for RDMA_READs with ConnectX-2. + * + * Also, still make sure to have at least two SGEs for + * outgoing control PDU responses. */ - attr.cap.max_send_sge = device->dev_attr.max_sge - 2; + attr.cap.max_send_sge = max(2, device->dev_attr.max_sge - 2); isert_conn->max_sge = attr.cap.max_send_sge; attr.cap.max_recv_sge = 1; -- cgit v0.10.2 From 3b726ae2de02a406cc91903f80132daee37b6f1b Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 28 Oct 2014 13:45:03 -0700 Subject: iser-target: Handle DEVICE_REMOVAL event on network portal listener correctly In this case the cm_id->context is the isert_np, and the cm_id->qp is NULL, so use that to distinct the cases. Since we don't expect any other events on this cm_id we can just return -1 for explicit termination of the cm_id by the cma layer. Signed-off-by: Sagi Grimberg Cc: # 3.10+ Signed-off-by: Nicholas Bellinger diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index ef5b22b..a6c7b39 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -806,14 +806,25 @@ wake_up: complete(&isert_conn->conn_wait); } -static void +static int isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect) { - struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context; + struct isert_conn *isert_conn; + + if (!cma_id->qp) { + struct isert_np *isert_np = cma_id->context; + + isert_np->np_cm_id = NULL; + return -1; + } + + isert_conn = (struct isert_conn *)cma_id->context; isert_conn->disconnect = disconnect; INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work); schedule_work(&isert_conn->conn_logout_work); + + return 0; } static int @@ -828,6 +839,9 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: ret = isert_connect_request(cma_id, event); + if (ret) + pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n", + event->event, ret); break; case RDMA_CM_EVENT_ESTABLISHED: isert_connected_handler(cma_id); @@ -837,7 +851,7 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */ disconnect = true; case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* FALLTHRU */ - isert_disconnected_handler(cma_id, disconnect); + ret = isert_disconnected_handler(cma_id, disconnect); break; case RDMA_CM_EVENT_CONNECT_ERROR: default: @@ -845,12 +859,6 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) break; } - if (ret != 0) { - pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n", - event->event, ret); - dump_stack(); - } - return ret; } @@ -3193,7 +3201,8 @@ isert_free_np(struct iscsi_np *np) { struct isert_np *isert_np = (struct isert_np *)np->np_context; - rdma_destroy_id(isert_np->np_cm_id); + if (isert_np->np_cm_id) + rdma_destroy_id(isert_np->np_cm_id); np->np_context = NULL; kfree(isert_np); -- cgit v0.10.2 From b6932ee35f1c1364dcea0e691b2feb912a6777e5 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 24 Oct 2014 15:18:26 -0700 Subject: target: return CONFLICT only when SA key unmatched PREEMPT (and PREEMPT AND ABORT) should return CONFLICT iff a specified SERVICE ACTION RESERVATION KEY is specified and matches no existing persistent reservation. Without this patch, a PREEMPT will return CONFLICT if either all reservations are held by the initiator (self preemption) or there is nothing to preempt. According to the spec, both of these cases should succeed. Signed-off-by: Steven Allen Signed-off-by: Nicholas Bellinger diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 8c60a1a..9f93b82 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -2738,7 +2738,8 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder; struct t10_reservation *pr_tmpl = &dev->t10_pr; u32 pr_res_mapped_lun = 0; - int all_reg = 0, calling_it_nexus = 0, released_regs = 0; + int all_reg = 0, calling_it_nexus = 0; + bool sa_res_key_unmatched = sa_res_key != 0; int prh_type = 0, prh_scope = 0; if (!se_sess) @@ -2813,6 +2814,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, if (!all_reg) { if (pr_reg->pr_res_key != sa_res_key) continue; + sa_res_key_unmatched = false; calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0; pr_reg_nacl = pr_reg->pr_reg_nacl; @@ -2820,7 +2822,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, __core_scsi3_free_registration(dev, pr_reg, (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL, calling_it_nexus); - released_regs++; } else { /* * Case for any existing all registrants type @@ -2838,6 +2839,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, if ((sa_res_key) && (pr_reg->pr_res_key != sa_res_key)) continue; + sa_res_key_unmatched = false; calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0; if (calling_it_nexus) @@ -2848,7 +2850,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, __core_scsi3_free_registration(dev, pr_reg, (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL, 0); - released_regs++; } if (!calling_it_nexus) core_scsi3_ua_allocate(pr_reg_nacl, @@ -2863,7 +2864,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key, * registered reservation key, then the device server shall * complete the command with RESERVATION CONFLICT status. */ - if (!released_regs) { + if (sa_res_key_unmatched) { spin_unlock(&dev->dev_reservation_lock); core_scsi3_put_pr_reg(pr_reg_n); return TCM_RESERVATION_CONFLICT; -- cgit v0.10.2 From e369086968157415aeb11af3b57cd998c6721603 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 3 Nov 2014 16:04:12 +0530 Subject: ALSA: echoaudio: add reference of struct echoaudio added reference of struct echoaudio to free_firmware function. this structure will be later used to get a reference of the card when converting snd_printk to dev_* in the next patch of the series. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index d82321f..db1b247 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -69,7 +69,8 @@ static int get_firmware(const struct firmware **fw_entry, -static void free_firmware(const struct firmware *fw_entry) +static void free_firmware(const struct firmware *fw_entry, + struct echoaudio *chip) { #ifdef CONFIG_PM_SLEEP DE_ACT(("firmware not released (kept in cache)\n")); diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index b86b88d..a4f112a 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -468,7 +468,8 @@ static int wait_handshake(struct echoaudio *chip); static int send_vector(struct echoaudio *chip, u32 command); static int get_firmware(const struct firmware **fw_entry, struct echoaudio *chip, const short fw_index); -static void free_firmware(const struct firmware *fw_entry); +static void free_firmware(const struct firmware *fw_entry, + struct echoaudio *chip); #ifdef ECHOCARD_HAS_MIDI static int enable_midi_input(struct echoaudio *chip, char enable); diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 5a6a217..977b2bd 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -206,12 +206,12 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) } DE_INIT(("ASIC loaded\n")); - free_firmware(fw); + free_firmware(fw, chip); return 0; la_error: DE_INIT(("failed on write_dsp\n")); - free_firmware(fw); + free_firmware(fw, chip); return -EIO; } @@ -317,11 +317,11 @@ static int install_resident_loader(struct echoaudio *chip) } DE_INIT(("Resident loader successfully installed\n")); - free_firmware(fw); + free_firmware(fw, chip); return 0; irl_error: - free_firmware(fw); + free_firmware(fw, chip); return -EIO; } @@ -491,7 +491,7 @@ static int load_firmware(struct echoaudio *chip) if (err < 0) return err; err = load_dsp(chip, (u16 *)fw->data); - free_firmware(fw); + free_firmware(fw, chip); if (err < 0) return err; -- cgit v0.10.2 From b5b4a41b392960010fccf1f9ccf8334d612bd450 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Mon, 3 Nov 2014 16:04:13 +0530 Subject: ALSA: echoaudio: remove all snd_printk removed all references of snd_printk with the standard dev_* macro. [a few places degraded to dev_dbg(), too -- tiwai] Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index 20c7cbc..c94e92e 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -33,12 +33,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Darla20\n")); + dev_dbg(chip->card->dev, "init_hw() - Darla20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw: could not initialize DSP comm page\n"); return err; } @@ -57,7 +58,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw: done\n"); return err; } diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index 6da6663..b1272f88 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -33,12 +33,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Darla24\n")); + dev_dbg(chip->card->dev, "init_hw() - Darla24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw: could not initialize DSP comm page\n"); return err; } @@ -56,7 +57,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw: done\n"); return err; } @@ -128,15 +129,17 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GD24_8000; break; default: - DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n", - rate)); + dev_err(chip->card->dev, + "set_sample_rate: Error, invalid sample rate %d\n", + rate); return -EINVAL; } if (wait_handshake(chip)) return -EIO; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); chip->sample_rate = rate; /* Override the sample rate if this card is set to Echo sync. */ diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index 3cdc2ee..bc37168 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -46,12 +46,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) int err; local_irq_enable(); - DE_INIT(("init_hw() - Echo3G\n")); + dev_dbg(chip->card->dev, "init_hw() - Echo3G\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -98,7 +99,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index db1b247..1ef29e5 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -48,13 +48,16 @@ static int get_firmware(const struct firmware **fw_entry, #ifdef CONFIG_PM_SLEEP if (chip->fw_cache[fw_index]) { - DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data)); + dev_dbg(chip->card->dev, + "firmware requested: %s is cached\n", + card_fw[fw_index].data); *fw_entry = chip->fw_cache[fw_index]; return 0; } #endif - DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data)); + dev_dbg(chip->card->dev, + "firmware requested: %s\n", card_fw[fw_index].data); snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data); err = request_firmware(fw_entry, name, pci_device(chip)); if (err < 0) @@ -73,10 +76,10 @@ static void free_firmware(const struct firmware *fw_entry, struct echoaudio *chip) { #ifdef CONFIG_PM_SLEEP - DE_ACT(("firmware not released (kept in cache)\n")); + dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); #else release_firmware(fw_entry); - DE_ACT(("firmware released\n")); + dev_dbg(chip->card->dev, "firmware released\n"); #endif } @@ -90,10 +93,10 @@ static void free_firmware_cache(struct echoaudio *chip) for (i = 0; i < 8 ; i++) if (chip->fw_cache[i]) { release_firmware(chip->fw_cache[i]); - DE_ACT(("release_firmware(%d)\n", i)); + dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - DE_ACT(("firmware_cache released\n")); + dev_dbg(chip->card->dev, "firmware_cache released\n"); #endif } @@ -287,7 +290,7 @@ static int pcm_open(struct snd_pcm_substream *substream, /* Set up hw capabilities and contraints */ memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware)); - DE_HWP(("max_channels=%d\n", max_channels)); + dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels); pipe->constr.list = channels_list; pipe->constr.mask = 0; for (i = 0; channels_list[i] <= max_channels; i++); @@ -337,7 +340,7 @@ static int pcm_open(struct snd_pcm_substream *substream, if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), PAGE_SIZE, &pipe->sgpage)) < 0) { - DE_HWP(("s-g list allocation failed\n")); + dev_err(chip->card->dev, "s-g list allocation failed\n"); return err; } @@ -351,7 +354,7 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err; - DE_ACT(("pcm_analog_in_open\n")); + dev_dbg(chip->card->dev, "pcm_analog_in_open\n"); if ((err = pcm_open(substream, num_analog_busses_in(chip) - substream->number)) < 0) return err; @@ -368,9 +371,9 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream) atomic_inc(&chip->opencount); if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; - DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n", + dev_dbg(chip->card->dev, "pcm_analog_in_open cs=%d oc=%d r=%d\n", chip->can_set_rate, atomic_read(&chip->opencount), - chip->sample_rate)); + chip->sample_rate); return 0; } @@ -386,7 +389,7 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream) #else max_channels = num_analog_busses_out(chip); #endif - DE_ACT(("pcm_analog_out_open\n")); + dev_dbg(chip->card->dev, "pcm_analog_out_open\n"); if ((err = pcm_open(substream, max_channels - substream->number)) < 0) return err; if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, @@ -404,9 +407,9 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream) atomic_inc(&chip->opencount); if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; - DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n", + dev_dbg(chip->card->dev, "pcm_analog_out_open cs=%d oc=%d r=%d\n", chip->can_set_rate, atomic_read(&chip->opencount), - chip->sample_rate)); + chip->sample_rate); return 0; } @@ -419,7 +422,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - DE_ACT(("pcm_digital_in_open\n")); + dev_dbg(chip->card->dev, "pcm_digital_in_open\n"); max_channels = num_digital_busses_in(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -461,7 +464,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - DE_ACT(("pcm_digital_out_open\n")); + dev_dbg(chip->card->dev, "pcm_digital_out_open\n"); max_channels = num_digital_busses_out(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -508,18 +511,18 @@ static int pcm_close(struct snd_pcm_substream *substream) /* Nothing to do here. Audio is already off and pipe will be * freed by its callback */ - DE_ACT(("pcm_close\n")); + dev_dbg(chip->card->dev, "pcm_close\n"); atomic_dec(&chip->opencount); oc = atomic_read(&chip->opencount); - DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc, - chip->can_set_rate, chip->rate_set)); + dev_dbg(chip->card->dev, "pcm_close oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set); if (oc < 2) chip->can_set_rate = 1; if (oc == 0) chip->rate_set = 0; - DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc, - chip->can_set_rate,chip->rate_set)); + dev_dbg(chip->card->dev, "pcm_close2 oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set); return 0; } @@ -543,7 +546,7 @@ static int init_engine(struct snd_pcm_substream *substream, */ spin_lock_irq(&chip->lock); if (pipe->index >= 0) { - DE_HWP(("hwp_ie free(%d)\n", pipe->index)); + dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index); err = free_pipes(chip, pipe); snd_BUG_ON(err); chip->substream[pipe->index] = NULL; @@ -552,16 +555,17 @@ static int init_engine(struct snd_pcm_substream *substream, err = allocate_pipes(chip, pipe, pipe_index, interleave); if (err < 0) { spin_unlock_irq(&chip->lock); - DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n", - pipe_index, err)); + dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n", + pipe_index, err); return err; } spin_unlock_irq(&chip->lock); - DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index)); + dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index); - DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", + dev_dbg(chip->card->dev, + "pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", params_buffer_bytes(hw_params), params_periods(hw_params), - params_period_bytes(hw_params))); + params_period_bytes(hw_params)); err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) { @@ -616,7 +620,7 @@ static int init_engine(struct snd_pcm_substream *substream, spin_lock_irq(&chip->lock); set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); spin_unlock_irq(&chip->lock); - DE_HWP(("pcm_hw_params ok\n")); + dev_dbg(chip->card->dev, "pcm_hw_params ok\n"); return 0; } @@ -680,14 +684,14 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) spin_lock_irq(&chip->lock); if (pipe->index >= 0) { - DE_HWP(("pcm_hw_free(%d)\n", pipe->index)); + dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index); free_pipes(chip, pipe); chip->substream[pipe->index] = NULL; pipe->index = -1; } spin_unlock_irq(&chip->lock); - DE_HWP(("pcm_hw_freed\n")); + dev_dbg(chip->card->dev, "pcm_hw_freed\n"); snd_pcm_lib_free_pages(substream); return 0; } @@ -701,8 +705,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream) struct audioformat format; int pipe_index = ((struct audiopipe *)runtime->private_data)->index; - DE_HWP(("Prepare rate=%d format=%d channels=%d\n", - runtime->rate, runtime->format, runtime->channels)); + dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n", + runtime->rate, runtime->format, runtime->channels); format.interleave = runtime->channels; format.data_are_bigendian = 0; format.mono_to_stereo = 0; @@ -722,8 +726,9 @@ static int pcm_prepare(struct snd_pcm_substream *substream) format.bits_per_sample = 32; break; default: - DE_HWP(("Prepare error: unsupported format %d\n", - runtime->format)); + dev_err(chip->card->dev, + "Prepare error: unsupported format %d\n", + runtime->format); return -EINVAL; } @@ -758,10 +763,10 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - DE_ACT(("pcm_trigger resume\n")); + dev_dbg(chip->card->dev, "pcm_trigger resume\n"); case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - DE_ACT(("pcm_trigger start\n")); + dev_dbg(chip->card->dev, "pcm_trigger start\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -783,9 +788,9 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) chip->pipe_cyclic_mask); break; case SNDRV_PCM_TRIGGER_SUSPEND: - DE_ACT(("pcm_trigger suspend\n")); + dev_dbg(chip->card->dev, "pcm_trigger suspend\n"); case SNDRV_PCM_TRIGGER_STOP: - DE_ACT(("pcm_trigger stop\n")); + dev_dbg(chip->card->dev, "pcm_trigger stop\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -795,7 +800,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) err = stop_transport(chip, channelmask); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - DE_ACT(("pcm_trigger pause\n")); + dev_dbg(chip->card->dev, "pcm_trigger pause\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -932,7 +937,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Analog PCM ok\n")); + dev_dbg(chip->card->dev, "Analog PCM ok\n"); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital inputs, no outputs */ @@ -945,7 +950,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Digital PCM ok\n")); + dev_dbg(chip->card->dev, "Digital PCM ok\n"); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #else /* ECHOCARD_HAS_VMIXER */ @@ -967,7 +972,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Analog PCM ok\n")); + dev_dbg(chip->card->dev, "Analog PCM ok\n"); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital i/o */ @@ -982,7 +987,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Digital PCM ok\n")); + dev_dbg(chip->card->dev, "Digital PCM ok\n"); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #endif /* ECHOCARD_HAS_VMIXER */ @@ -1475,7 +1480,8 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->clock_src_ctl->id); - DE_ACT(("SDM() =%d\n", changed)); + dev_dbg(chip->card->dev, + "SDM() =%d\n", changed); } if (changed >= 0) changed = 1; /* No errors */ @@ -1602,7 +1608,8 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, } if (changed < 0) - DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed)); + dev_dbg(chip->card->dev, + "seticlk val%d err 0x%x\n", dclock, changed); return changed; } @@ -1859,7 +1866,7 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) #ifdef ECHOCARD_HAS_MIDI if (st > 0 && chip->midi_in) { snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); - DE_MID(("rawmidi_iread=%d\n", st)); + dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st); } #endif return IRQ_HANDLED; @@ -1874,10 +1881,10 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) static int snd_echo_free(struct echoaudio *chip) { - DE_INIT(("Stop DSP...\n")); + dev_dbg(chip->card->dev, "Stop DSP...\n"); if (chip->comm_page) rest_in_peace(chip); - DE_INIT(("Stopped.\n")); + dev_dbg(chip->card->dev, "Stopped.\n"); if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -1891,14 +1898,14 @@ static int snd_echo_free(struct echoaudio *chip) if (chip->iores) release_and_free_resource(chip->iores); - DE_INIT(("MMIO freed.\n")); + dev_dbg(chip->card->dev, "MMIO freed.\n"); pci_disable_device(chip->pci); /* release chip data */ free_firmware_cache(chip); kfree(chip); - DE_INIT(("Chip freed.\n")); + dev_dbg(chip->card->dev, "Chip freed.\n"); return 0; } @@ -1908,7 +1915,7 @@ static int snd_echo_dev_free(struct snd_device *device) { struct echoaudio *chip = device->device_data; - DE_INIT(("snd_echo_dev_free()...\n")); + dev_dbg(chip->card->dev, "snd_echo_dev_free()...\n"); return snd_echo_free(chip); } @@ -1941,7 +1948,7 @@ static int snd_echo_create(struct snd_card *card, pci_disable_device(pci); return -ENOMEM; } - DE_INIT(("chip=%p\n", chip)); + dev_dbg(card->dev, "chip=%p\n", chip); spin_lock_init(&chip->lock); chip->card = card; chip->pci = pci; @@ -1978,8 +1985,8 @@ static int snd_echo_create(struct snd_card *card, return -EBUSY; } chip->irq = pci->irq; - DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n", - chip->pci, chip->irq, chip->pci->subsystem_device)); + dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n", + chip->pci, chip->irq, chip->pci->subsystem_device); /* Create the DSP comm page - this is the area of memory used for most of the communication with the DSP, which accesses it via bus mastering */ @@ -1997,11 +2004,11 @@ static int snd_echo_create(struct snd_card *card, if (err >= 0) err = set_mixer_defaults(chip); if (err < 0) { - DE_INIT(("init_hw err=%d\n", err)); + dev_err(card->dev, "init_hw err=%d\n", err); snd_echo_free(chip); return err; } - DE_INIT(("Card init OK\n")); + dev_dbg(card->dev, "Card init OK\n"); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_echo_free(chip); @@ -2031,7 +2038,7 @@ static int snd_echo_probe(struct pci_dev *pci, return -ENOENT; } - DE_INIT(("Echoaudio driver starting...\n")); + dev_dbg(&pci->dev, "Echoaudio driver starting...\n"); i = 0; err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); @@ -2184,7 +2191,7 @@ static int snd_echo_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct echoaudio *chip = dev_get_drvdata(dev); - DE_INIT(("suspend start\n")); + dev_dbg(dev, "suspend start\n"); snd_pcm_suspend_all(chip->analog_pcm); snd_pcm_suspend_all(chip->digital_pcm); @@ -2211,7 +2218,7 @@ static int snd_echo_suspend(struct device *dev) pci_save_state(pci); pci_disable_device(pci); - DE_INIT(("suspend done\n")); + dev_dbg(dev, "suspend done\n"); return 0; } @@ -2225,7 +2232,7 @@ static int snd_echo_resume(struct device *dev) u32 pipe_alloc_mask; int err; - DE_INIT(("resume start\n")); + dev_dbg(dev, "resume start\n"); pci_restore_state(pci); commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL); if (commpage_bak == NULL) @@ -2236,11 +2243,11 @@ static int snd_echo_resume(struct device *dev) err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); if (err < 0) { kfree(commpage_bak); - DE_INIT(("resume init_hw err=%d\n", err)); + dev_err(dev, "resume init_hw err=%d\n", err); snd_echo_free(chip); return err; } - DE_INIT(("resume init OK\n")); + dev_dbg(dev, "resume init OK\n"); /* Temporarily set chip->pipe_alloc_mask=0 otherwise * restore_dsp_settings() fails. @@ -2253,7 +2260,7 @@ static int snd_echo_resume(struct device *dev) kfree(commpage_bak); return err; } - DE_INIT(("resume restore OK\n")); + dev_dbg(dev, "resume restore OK\n"); memcpy(&commpage->audio_format, &commpage_bak->audio_format, sizeof(commpage->audio_format)); @@ -2270,7 +2277,7 @@ static int snd_echo_resume(struct device *dev) return -EBUSY; } chip->irq = pci->irq; - DE_INIT(("resume irq=%d\n", chip->irq)); + dev_dbg(dev, "resume irq=%d\n", chip->irq); #ifdef ECHOCARD_HAS_MIDI if (chip->midi_input_enabled) @@ -2279,7 +2286,7 @@ static int snd_echo_resume(struct device *dev) snd_echo_midi_output_trigger(chip->midi_out, 1); #endif - DE_INIT(("resume done\n")); + dev_dbg(dev, "resume done\n"); return 0; } diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index a4f112a..3251522 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -295,34 +295,6 @@ #define PIPE_STATE_PENDING 3 /* Pipe has pending start */ -/* Debug initialization */ -#ifdef CONFIG_SND_DEBUG -#define DE_INIT(x) snd_printk x -#else -#define DE_INIT(x) -#endif - -/* Debug hw_params callbacks */ -#ifdef CONFIG_SND_DEBUG -#define DE_HWP(x) snd_printk x -#else -#define DE_HWP(x) -#endif - -/* Debug normal activity (open, start, stop...) */ -#ifdef CONFIG_SND_DEBUG -#define DE_ACT(x) snd_printk x -#else -#define DE_ACT(x) -#endif - -/* Debug midi activity */ -#ifdef CONFIG_SND_DEBUG -#define DE_MID(x) snd_printk x -#else -#define DE_MID(x) -#endif - struct audiopipe { volatile u32 *dma_counter; /* Commpage register that contains diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index 658db44..50a2169 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -51,7 +51,7 @@ static int check_asic_status(struct echoaudio *chip) } box_status = le32_to_cpu(chip->comm_page->ext_box_status); - DE_INIT(("box_status=%x\n", box_status)); + dev_dbg(chip->card->dev, "box_status=%x\n", box_status); if (box_status == E3G_ASIC_NOT_LOADED) return -ENODEV; @@ -76,7 +76,8 @@ static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, if (wait_handshake(chip)) return -EIO; - DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq)); + dev_dbg(chip->card->dev, + "WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq); ctl = cpu_to_le32(ctl); frq = cpu_to_le32(frq); @@ -89,7 +90,7 @@ static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); } - DE_ACT(("WriteControlReg: not written, no change\n")); + dev_dbg(chip->card->dev, "WriteControlReg: not written, no change\n"); return 0; } @@ -258,8 +259,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -313,7 +314,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg)); + dev_dbg(chip->card->dev, + "SetSampleRate: %d clock %x\n", rate, control_reg); /* Tell the DSP about it - DSP reads both control reg & freq reg */ return write_control_reg(chip, control_reg, frq_reg, 0); @@ -326,7 +328,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - DE_ACT(("set_input_clock:\n")); + dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -335,13 +337,13 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Echo3G clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Echo3G clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Echo3G clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Echo3G clock to SPDIF\n"); control_reg |= E3G_SPDIF_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96) control_reg |= E3G_DOUBLE_SPEED_MODE; @@ -351,12 +353,12 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Echo3G clock to ADAT\n")); + dev_dbg(chip->card->dev, "Set Echo3G clock to ADAT\n"); control_reg |= E3G_ADAT_CLOCK; control_reg &= ~E3G_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Echo3G clock to WORD\n")); + dev_dbg(chip->card->dev, "Set Echo3G clock to WORD\n"); control_reg |= E3G_WORD_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96) control_reg |= E3G_DOUBLE_SPEED_MODE; @@ -364,7 +366,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~E3G_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Echo3G\n", clock); return -EINVAL; } @@ -392,7 +395,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -427,6 +431,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode)); + dev_dbg(chip->card->dev, "set_digital_mode(%d)\n", chip->digital_mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 977b2bd..ba9d4f1 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -80,7 +80,7 @@ static int send_vector(struct echoaudio *chip, u32 command) udelay(1); } - DE_ACT((KERN_ERR "timeout on send_vector\n")); + dev_err(chip->card->dev, "timeout on send_vector\n"); return -EBUSY; } @@ -104,7 +104,7 @@ static int write_dsp(struct echoaudio *chip, u32 data) } chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n")); + dev_dbg(chip->card->dev, "write_dsp: Set bad_board to TRUE\n"); return -EIO; } @@ -127,7 +127,7 @@ static int read_dsp(struct echoaudio *chip, u32 *data) } chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n")); + dev_err(chip->card->dev, "read_dsp: Set bad_board to TRUE\n"); return -EIO; } @@ -154,8 +154,9 @@ static int read_sn(struct echoaudio *chip) return -EIO; } } - DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n", - sn[0], sn[1], sn[2], sn[3], sn[4])); + dev_dbg(chip->card->dev, + "Read serial number %08x %08x %08x %08x %08x\n", + sn[0], sn[1], sn[2], sn[3], sn[4]); return 0; } @@ -205,12 +206,12 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) goto la_error; } - DE_INIT(("ASIC loaded\n")); + dev_dbg(chip->card->dev, "ASIC loaded\n"); free_firmware(fw, chip); return 0; la_error: - DE_INIT(("failed on write_dsp\n")); + dev_err(chip->card->dev, "failed on write_dsp\n"); free_firmware(fw, chip); return -EIO; } @@ -241,8 +242,9 @@ static int install_resident_loader(struct echoaudio *chip) loader is already installed, host flag 5 will be on. */ status = get_dsp_register(chip, CHI32_STATUS_REG); if (status & CHI32_STATUS_REG_HF5) { - DE_INIT(("Resident loader already installed; status is 0x%x\n", - status)); + dev_dbg(chip->card->dev, + "Resident loader already installed; status is 0x%x\n", + status); return 0; } @@ -283,12 +285,14 @@ static int install_resident_loader(struct echoaudio *chip) /* Write the count to the DSP */ if (write_dsp(chip, words)) { - DE_INIT(("install_resident_loader: Failed to write word count!\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write word count!\n"); goto irl_error; } /* Write the DSP address */ if (write_dsp(chip, address)) { - DE_INIT(("install_resident_loader: Failed to write DSP address!\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write DSP address!\n"); goto irl_error; } /* Write out this block of code to the DSP */ @@ -297,7 +301,8 @@ static int install_resident_loader(struct echoaudio *chip) data = ((u32)code[index] << 16) + code[index + 1]; if (write_dsp(chip, data)) { - DE_INIT(("install_resident_loader: Failed to write DSP code\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write DSP code\n"); goto irl_error; } index += 2; @@ -312,11 +317,11 @@ static int install_resident_loader(struct echoaudio *chip) } if (i == 200) { - DE_INIT(("Resident loader failed to set HF5\n")); + dev_err(chip->card->dev, "Resident loader failed to set HF5\n"); goto irl_error; } - DE_INIT(("Resident loader successfully installed\n")); + dev_dbg(chip->card->dev, "Resident loader successfully installed\n"); free_firmware(fw, chip); return 0; @@ -334,14 +339,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code) int index, words, i; if (chip->dsp_code == code) { - DE_INIT(("DSP is already loaded!\n")); + dev_warn(chip->card->dev, "DSP is already loaded!\n"); return 0; } chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */ - DE_INIT(("load_dsp: Set bad_board to TRUE\n")); + dev_dbg(chip->card->dev, "load_dsp: Set bad_board to TRUE\n"); /* If this board requires a resident loader, install it. */ #ifdef DSP_56361 @@ -351,7 +356,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code) /* Send software reset command */ if (send_vector(chip, DSP_VC_RESET) < 0) { - DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n")); + dev_err(chip->card->dev, + "LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"); return -EIO; } /* Delay 10us */ @@ -366,7 +372,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code) } if (i == 1000) { - DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n")); + dev_err(chip->card->dev, + "load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"); return -EIO; } @@ -403,29 +410,34 @@ static int load_dsp(struct echoaudio *chip, u16 *code) index += 2; if (write_dsp(chip, words) < 0) { - DE_INIT(("load_dsp: failed to write number of DSP words\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write number of DSP words\n"); return -EIO; } if (write_dsp(chip, address) < 0) { - DE_INIT(("load_dsp: failed to write DSP address\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP address\n"); return -EIO; } if (write_dsp(chip, mem_type) < 0) { - DE_INIT(("load_dsp: failed to write DSP memory type\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP memory type\n"); return -EIO; } /* Code */ for (i = 0; i < words; i++, index+=2) { data = ((u32)code[index] << 16) + code[index + 1]; if (write_dsp(chip, data) < 0) { - DE_INIT(("load_dsp: failed to write DSP data\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP data\n"); return -EIO; } } } if (write_dsp(chip, 0) < 0) { /* We're done!!! */ - DE_INIT(("load_dsp: Failed to write final zero\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write final zero\n"); return -EIO; } udelay(10); @@ -438,12 +450,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code) get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00); if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) { - DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"); return -EIO; } if (write_dsp(chip, chip->comm_page_phys) < 0) { - DE_INIT(("load_dsp: Failed to write comm page address\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write comm page address\n"); return -EIO; } @@ -452,19 +466,21 @@ static int load_dsp(struct echoaudio *chip, u16 *code) We don't actually use the serial number but we have to get it as part of the DSP init voodoo. */ if (read_sn(chip) < 0) { - DE_INIT(("load_dsp: Failed to read serial number\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to read serial number\n"); return -EIO; } chip->dsp_code = code; /* Show which DSP code loaded */ chip->bad_board = FALSE; /* DSP OK */ - DE_INIT(("load_dsp: OK!\n")); + dev_dbg(chip->card->dev, "load_dsp: OK!\n"); return 0; } udelay(100); } - DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n")); + dev_err(chip->card->dev, + "load_dsp: DSP load timed out waiting for HF4\n"); return -EIO; } @@ -658,7 +674,7 @@ static void get_audio_meters(struct echoaudio *chip, long *meters) static int restore_dsp_rettings(struct echoaudio *chip) { int i, o, err; - DE_INIT(("restore_dsp_settings\n")); + dev_dbg(chip->card->dev, "restore_dsp_settings\n"); if ((err = check_asic_status(chip)) < 0) return err; @@ -755,7 +771,7 @@ static int restore_dsp_rettings(struct echoaudio *chip) if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0) return -EIO; - DE_INIT(("restore_dsp_rettings done\n")); + dev_dbg(chip->card->dev, "restore_dsp_rettings done\n"); return 0; } @@ -835,7 +851,8 @@ static void set_audio_format(struct echoaudio *chip, u16 pipe_index, break; } } - DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format)); + dev_dbg(chip->card->dev, + "set_audio_format[%d] = %x\n", pipe_index, dsp_format); chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format); } @@ -848,7 +865,7 @@ Same thing for pause_ and stop_ -trasport below. */ static int start_transport(struct echoaudio *chip, u32 channel_mask, u32 cyclic_mask) { - DE_ACT(("start_transport %x\n", channel_mask)); + dev_dbg(chip->card->dev, "start_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -866,7 +883,7 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask, return 0; } - DE_ACT(("start_transport: No pipes to start!\n")); + dev_err(chip->card->dev, "start_transport: No pipes to start!\n"); return -EINVAL; } @@ -874,7 +891,7 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask, static int pause_transport(struct echoaudio *chip, u32 channel_mask) { - DE_ACT(("pause_transport %x\n", channel_mask)); + dev_dbg(chip->card->dev, "pause_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -893,7 +910,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask) return 0; } - DE_ACT(("pause_transport: No pipes to stop!\n")); + dev_warn(chip->card->dev, "pause_transport: No pipes to stop!\n"); return 0; } @@ -901,7 +918,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask) static int stop_transport(struct echoaudio *chip, u32 channel_mask) { - DE_ACT(("stop_transport %x\n", channel_mask)); + dev_dbg(chip->card->dev, "stop_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -920,7 +937,7 @@ static int stop_transport(struct echoaudio *chip, u32 channel_mask) return 0; } - DE_ACT(("stop_transport: No pipes to stop!\n")); + dev_warn(chip->card->dev, "stop_transport: No pipes to stop!\n"); return 0; } @@ -937,7 +954,8 @@ static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) stopped and unallocated. */ static int rest_in_peace(struct echoaudio *chip) { - DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask)); + dev_dbg(chip->card->dev, + "rest_in_peace() open=%x\n", chip->pipe_alloc_mask); /* Stops all active pipes (just to be sure) */ stop_transport(chip, chip->active_mask); @@ -965,7 +983,8 @@ static int init_dsp_comm_page(struct echoaudio *chip) { /* Check if the compiler added extra padding inside the structure */ if (offsetof(struct comm_page, midi_output) != 0xbe0) { - DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n")); + dev_err(chip->card->dev, + "init_dsp_comm_page() - Invalid struct comm_page structure\n"); return -EPERM; } @@ -999,7 +1018,7 @@ static int init_dsp_comm_page(struct echoaudio *chip) */ static int init_line_levels(struct echoaudio *chip) { - DE_INIT(("init_line_levels\n")); + dev_dbg(chip->card->dev, "init_line_levels\n"); memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain)); memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain)); memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain)); @@ -1051,7 +1070,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, u32 channel_mask; char is_cyclic; - DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave)); + dev_dbg(chip->card->dev, + "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave); if (chip->bad_board) return -EIO; @@ -1061,7 +1081,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, for (channel_mask = i = 0; i < interleave; i++) channel_mask |= 1 << (pipe_index + i); if (chip->pipe_alloc_mask & channel_mask) { - DE_ACT(("allocate_pipes: channel already open\n")); + dev_err(chip->card->dev, + "allocate_pipes: channel already open\n"); return -EAGAIN; } @@ -1078,7 +1099,7 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, it moves data. The DMA counter is in units of bytes, not samples. */ pipe->dma_counter = &chip->comm_page->position[pipe_index]; *pipe->dma_counter = 0; - DE_ACT(("allocate_pipes: ok\n")); + dev_dbg(chip->card->dev, "allocate_pipes: ok\n"); return pipe_index; } @@ -1089,7 +1110,7 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) u32 channel_mask; int i; - DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); + dev_dbg(chip->card->dev, "free_pipes: Pipe %d\n", pipe->index); if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index))) return -EINVAL; if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED)) @@ -1131,7 +1152,7 @@ static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, list[head].size = cpu_to_le32(length); pipe->sglist_head++; } else { - DE_ACT(("SGlist: too many fragments\n")); + dev_err(chip->card->dev, "SGlist: too many fragments\n"); return -ENOMEM; } return 0; diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c index afa2733..23a0994 100644 --- a/sound/pci/echoaudio/echoaudio_gml.c +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -46,7 +46,8 @@ static int check_asic_status(struct echoaudio *chip) /* The DSP will return a value to indicate whether or not the ASIC is currently loaded */ if (read_dsp(chip, &asic_status) < 0) { - DE_INIT(("check_asic_status: failed on read_dsp\n")); + dev_err(chip->card->dev, + "check_asic_status: failed on read_dsp\n"); chip->asic_loaded = FALSE; return -EIO; } @@ -68,7 +69,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force) else value &= ~GML_DIGITAL_IN_AUTO_MUTE; - DE_ACT(("write_control_reg: 0x%x\n", value)); + dev_dbg(chip->card->dev, "write_control_reg: 0x%x\n", value); /* Write the control register */ value = cpu_to_le32(value); @@ -91,7 +92,7 @@ If the auto-mute is disabled, the digital inputs are enabled regardless of what the input clock is set or what is connected. */ static int set_input_auto_mute(struct echoaudio *chip, int automute) { - DE_ACT(("set_input_auto_mute %d\n", automute)); + dev_dbg(chip->card->dev, "set_input_auto_mute %d\n", automute); chip->digital_in_automute = automute; @@ -194,7 +195,7 @@ static int set_professional_spdif(struct echoaudio *chip, char prof) if ((err = write_control_reg(chip, control_reg, FALSE))) return err; chip->professional_spdif = prof; - DE_ACT(("set_professional_spdif to %s\n", - prof ? "Professional" : "Consumer")); + dev_dbg(chip->card->dev, "set_professional_spdif to %s\n", + prof ? "Professional" : "Consumer"); return 0; } diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index d1615a0..a959eae 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -37,12 +37,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Gina20\n")); + dev_dbg(chip->card->dev, "init_hw() - Gina20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -62,7 +63,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -149,7 +150,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) static int set_input_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_input_clock:\n")); + dev_dbg(chip->card->dev, "set_input_clock:\n"); switch (clock) { case ECHO_CLOCK_INTERNAL: @@ -158,7 +159,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) chip->spdif_status = GD_SPDIF_STATUS_UNDEF; set_sample_rate(chip, chip->sample_rate); chip->input_clock = clock; - DE_ACT(("Set Gina clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Gina clock to INTERNAL\n"); break; case ECHO_CLOCK_SPDIF: chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN; @@ -166,7 +167,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) clear_handshake(chip); send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); chip->clock_state = GD_CLOCK_SPDIFIN; - DE_ACT(("Set Gina20 clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Gina20 clock to SPDIF\n"); chip->input_clock = clock; break; default: @@ -208,7 +209,7 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); + dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 98f7cfa..c8ea576 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -41,12 +41,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Gina24\n")); + dev_dbg(chip->card->dev, "init_hw() - Gina24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -78,7 +79,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -155,7 +156,7 @@ static int load_asic(struct echoaudio *chip) control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; err = write_control_reg(chip, control_reg, TRUE); } - DE_INIT(("load_asic() done\n")); + dev_dbg(chip->card->dev, "load_asic() done\n"); return err; } @@ -171,8 +172,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -217,7 +218,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GML_8KHZ; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -225,7 +227,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock); return write_control_reg(chip, control_reg, FALSE); } @@ -236,7 +238,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - DE_ACT(("set_input_clock:\n")); + dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -245,13 +247,13 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Gina24 clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Gina24 clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Gina24 clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Gina24 clock to SPDIF\n"); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -261,21 +263,22 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Gina24 clock to ADAT\n")); + dev_dbg(chip->card->dev, "Set Gina24 clock to ADAT\n"); control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC: - DE_ACT(("Set Gina24 clock to ESYNC\n")); + dev_dbg(chip->card->dev, "Set Gina24 clock to ESYNC\n"); control_reg |= GML_ESYNC_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC96: - DE_ACT(("Set Gina24 clock to ESYNC96\n")); + dev_dbg(chip->card->dev, "Set Gina24 clock to ESYNC96\n"); control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Gina24\n", clock); return -EINVAL; } @@ -304,7 +307,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -344,6 +348,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", chip->digital_mode)); + dev_dbg(chip->card->dev, + "set_digital_mode to %d\n", chip->digital_mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index 5e85f14..cdeb073 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -38,12 +38,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo\n")); + dev_dbg(chip->card->dev, "init_hw() - Indigo\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +61,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c index 2e4ab3e..ceda2d7 100644 --- a/sound/pci/echoaudio/indigo_express_dsp.c +++ b/sound/pci/echoaudio/indigo_express_dsp.c @@ -61,7 +61,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg |= clock; if (control_reg != old_control_reg) { - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); chip->comm_page->control_register = cpu_to_le32(control_reg); chip->sample_rate = rate; clear_handshake(chip); @@ -89,7 +90,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 68f3c8c..133915c 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -38,12 +38,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo DJ\n")); + dev_dbg(chip->card->dev, "init_hw() - Indigo DJ\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +61,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index bb9632c7..26cdfcf 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -35,13 +35,14 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo DJx\n")); + dev_dbg(chip->card->dev, "init_hw() - Indigo DJx\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX)) return -ENODEV; err = init_dsp_comm_page(chip); if (err < 0) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -59,7 +60,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index beb9a5b..5e6df7c 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -38,12 +38,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo IO\n")); + dev_dbg(chip->card->dev, "init_hw() - Indigo IO\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +61,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -118,7 +119,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index 394c6e76..90cdd27 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -35,13 +35,14 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo IOx\n")); + dev_dbg(chip->card->dev, "init_hw() - Indigo IOx\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX)) return -ENODEV; err = init_dsp_comm_page(chip); if (err < 0) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -59,7 +60,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 53ce946..7f0f6ea 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -40,12 +40,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Layla20\n")); + dev_dbg(chip->card->dev, "init_hw() - Layla20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -64,7 +65,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -121,7 +122,8 @@ static int check_asic_status(struct echoaudio *chip) /* The DSP will return a value to indicate whether or not the ASIC is currently loaded */ if (read_dsp(chip, &asic_status) < 0) { - DE_ACT(("check_asic_status: failed on read_dsp\n")); + dev_err(chip->card->dev, + "check_asic_status: failed on read_dsp\n"); return -EIO; } @@ -164,8 +166,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. Do not return failure, simply treat it as a non-event. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; return 0; @@ -174,7 +176,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) if (wait_handshake(chip)) return -EIO; - DE_ACT(("set_sample_rate(%d)\n", rate)); + dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate); chip->sample_rate = rate; chip->comm_page->sample_rate = cpu_to_le32(rate); clear_handshake(chip); @@ -188,29 +190,30 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) u16 clock; u32 rate; - DE_ACT(("set_input_clock:\n")); + dev_dbg(chip->card->dev, "set_input_clock:\n"); rate = 0; switch (clock_source) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Layla20 clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Layla20 clock to INTERNAL\n"); rate = chip->sample_rate; clock = LAYLA20_CLOCK_INTERNAL; break; case ECHO_CLOCK_SPDIF: - DE_ACT(("Set Layla20 clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Layla20 clock to SPDIF\n"); clock = LAYLA20_CLOCK_SPDIF; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Layla20 clock to WORD\n")); + dev_dbg(chip->card->dev, "Set Layla20 clock to WORD\n"); clock = LAYLA20_CLOCK_WORD; break; case ECHO_CLOCK_SUPER: - DE_ACT(("Set Layla20 clock to SUPER\n")); + dev_dbg(chip->card->dev, "Set Layla20 clock to SUPER\n"); clock = LAYLA20_CLOCK_SUPER; break; default: - DE_ACT(("Input clock 0x%x not supported for Layla24\n", - clock_source)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Layla24\n", + clock_source); return -EINVAL; } chip->input_clock = clock_source; @@ -229,7 +232,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) static int set_output_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_output_clock: %d\n", clock)); + dev_dbg(chip->card->dev, "set_output_clock: %d\n", clock); switch (clock) { case ECHO_CLOCK_SUPER: clock = LAYLA20_OUTPUT_CLOCK_SUPER; @@ -238,7 +241,7 @@ static int set_output_clock(struct echoaudio *chip, u16 clock) clock = LAYLA20_OUTPUT_CLOCK_WORD; break; default: - DE_ACT(("set_output_clock wrong clock\n")); + dev_err(chip->card->dev, "set_output_clock wrong clock\n"); return -EINVAL; } @@ -283,7 +286,7 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); + dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index 8c04164..eb8f218 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -40,12 +40,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Layla24\n")); + dev_dbg(chip->card->dev, "init_hw() - Layla24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -69,7 +70,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = init_line_levels(chip)) < 0) return err; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -117,7 +118,7 @@ static int load_asic(struct echoaudio *chip) if (chip->asic_loaded) return 1; - DE_INIT(("load_asic\n")); + dev_dbg(chip->card->dev, "load_asic\n"); /* Give the DSP a few milliseconds to settle down */ mdelay(10); @@ -151,7 +152,7 @@ static int load_asic(struct echoaudio *chip) err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, TRUE); - DE_INIT(("load_asic() done\n")); + dev_dbg(chip->card->dev, "load_asic() done\n"); return err; } @@ -167,8 +168,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -241,7 +242,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, control_reg); return write_control_reg(chip, control_reg, FALSE); } @@ -260,7 +262,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) /* Pick the new clock */ switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Layla24 clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Layla24 clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -269,7 +271,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_SPDIF_CLOCK; /* Layla24 doesn't support 96KHz S/PDIF */ control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Layla24 clock to SPDIF\n"); break; case ECHO_CLOCK_WORD: control_reg |= GML_WORD_CLOCK; @@ -277,17 +279,18 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_DOUBLE_SPEED_MODE; else control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to WORD\n")); + dev_dbg(chip->card->dev, "Set Layla24 clock to WORD\n"); break; case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to ADAT\n")); + dev_dbg(chip->card->dev, "Set Layla24 clock to ADAT\n"); break; default: - DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Layla24\n", clock); return -EINVAL; } @@ -353,7 +356,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) asic = FW_LAYLA24_2A_ASIC; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -393,6 +397,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", mode)); + dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index 6ebfa6e..ed2f21d 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -41,12 +41,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Mia\n")); + dev_dbg(chip->card->dev, "init_hw() - Mia\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -66,7 +67,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -126,7 +127,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -153,7 +155,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) static int set_input_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_input_clock(%d)\n", clock)); + dev_dbg(chip->card->dev, "set_input_clock(%d)\n", clock); if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL && clock != ECHO_CLOCK_SPDIF)) return -EINVAL; @@ -181,7 +183,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } @@ -211,7 +214,7 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); + dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index 7f4dfae..8d43c5a 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -36,7 +36,7 @@ /* Start and stop Midi input */ static int enable_midi_input(struct echoaudio *chip, char enable) { - DE_MID(("enable_midi_input(%d)\n", enable)); + dev_dbg(chip->card->dev, "enable_midi_input(%d)\n", enable); if (wait_handshake(chip)) return -EIO; @@ -74,7 +74,7 @@ static int write_midi(struct echoaudio *chip, u8 *data, int bytes) chip->comm_page->midi_out_free_count = 0; clear_handshake(chip); send_vector(chip, DSP_VC_MIDI_WRITE); - DE_MID(("write_midi: %d\n", bytes)); + dev_dbg(chip->card->dev, "write_midi: %d\n", bytes); return bytes; } @@ -157,7 +157,7 @@ static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = substream; - DE_MID(("rawmidi_iopen\n")); + dev_dbg(chip->card->dev, "rawmidi_iopen\n"); return 0; } @@ -183,7 +183,7 @@ static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = NULL; - DE_MID(("rawmidi_iclose\n")); + dev_dbg(chip->card->dev, "rawmidi_iclose\n"); return 0; } @@ -196,7 +196,7 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) chip->tinuse = 0; chip->midi_full = 0; chip->midi_out = substream; - DE_MID(("rawmidi_oopen\n")); + dev_dbg(chip->card->dev, "rawmidi_open\n"); return 0; } @@ -209,7 +209,7 @@ static void snd_echo_midi_output_write(unsigned long data) int bytes, sent, time; unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; - DE_MID(("snd_echo_midi_output_write\n")); + dev_dbg(chip->card->dev, "snd_echo_midi_output_write\n"); /* No interrupts are involved: we have to check at regular intervals if the card's output buffer has room for new data. */ sent = bytes = 0; @@ -218,7 +218,7 @@ static void snd_echo_midi_output_write(unsigned long data) if (!snd_rawmidi_transmit_empty(chip->midi_out)) { bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf, MIDI_OUT_BUFFER_SIZE - 1); - DE_MID(("Try to send %d bytes...\n", bytes)); + dev_dbg(chip->card->dev, "Try to send %d bytes...\n", bytes); sent = write_midi(chip, buf, bytes); if (sent < 0) { dev_err(chip->card->dev, @@ -227,12 +227,12 @@ static void snd_echo_midi_output_write(unsigned long data) sent = 9000; chip->midi_full = 1; } else if (sent > 0) { - DE_MID(("%d bytes sent\n", sent)); + dev_dbg(chip->card->dev, "%d bytes sent\n", sent); snd_rawmidi_transmit_ack(chip->midi_out, sent); } else { /* Buffer is full. DSP's internal buffer is 64 (128 ?) bytes long. Let's wait until half of them are sent */ - DE_MID(("Full\n")); + dev_dbg(chip->card->dev, "Full\n"); sent = 32; chip->midi_full = 1; } @@ -244,7 +244,8 @@ static void snd_echo_midi_output_write(unsigned long data) sent */ time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */ mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000); - DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000))); + dev_dbg(chip->card->dev, + "Timer armed(%d)\n", ((time * HZ + 999) / 1000)); } spin_unlock_irqrestore(&chip->lock, flags); } @@ -256,7 +257,7 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream { struct echoaudio *chip = substream->rmidi->private_data; - DE_MID(("snd_echo_midi_output_trigger(%d)\n", up)); + dev_dbg(chip->card->dev, "snd_echo_midi_output_trigger(%d)\n", up); spin_lock_irq(&chip->lock); if (up) { if (!chip->tinuse) { @@ -270,7 +271,7 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream chip->tinuse = 0; spin_unlock_irq(&chip->lock); del_timer_sync(&chip->timer); - DE_MID(("Timer removed\n")); + dev_dbg(chip->card->dev, "Timer removed\n"); return; } } @@ -287,7 +288,7 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_out = NULL; - DE_MID(("rawmidi_oclose\n")); + dev_dbg(chip->card->dev, "rawmidi_oclose\n"); return 0; } @@ -327,6 +328,6 @@ static int snd_echo_midi_create(struct snd_card *card, chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - DE_INIT(("MIDI ok\n")); + dev_dbg(chip->card->dev, "MIDI ok\n"); return 0; } diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index 6e6a7eb..cc46a8c 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -41,12 +41,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Mona\n")); + dev_dbg(chip->card->dev, "init_hw() - Mona\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -71,7 +72,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); + dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -202,8 +203,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_dbg(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -279,7 +280,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GML_8KHZ; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -287,7 +289,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); return write_control_reg(chip, control_reg, force_write); } @@ -299,7 +302,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) u32 control_reg, clocks_from_dsp; int err; - DE_ACT(("set_input_clock:\n")); + dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Prevent two simultaneous calls to switch_asic() */ if (atomic_read(&chip->opencount)) @@ -312,7 +315,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Mona clock to INTERNAL\n")); + dev_dbg(chip->card->dev, "Set Mona clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -324,7 +327,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) spin_lock_irq(&chip->lock); if (err < 0) return err; - DE_ACT(("Set Mona clock to SPDIF\n")); + dev_dbg(chip->card->dev, "Set Mona clock to SPDIF\n"); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -332,7 +335,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Mona clock to WORD\n")); + dev_dbg(chip->card->dev, "Set Mona clock to WORD\n"); spin_unlock_irq(&chip->lock); err = switch_asic(chip, clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96); @@ -346,14 +349,15 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ADAT: - DE_ACT(("Set Mona clock to ADAT\n")); + dev_dbg(chip->card->dev, "Set Mona clock to ADAT\n"); if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Mona\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Mona\n", clock); return -EINVAL; } @@ -381,7 +385,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -422,6 +427,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", mode)); + dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode); return incompatible_clock; } -- cgit v0.10.2 From 31604d35db18c1382c7ee9fa836ff9ab0b4d2751 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 3 Nov 2014 14:54:36 +0100 Subject: ALSA: emu10k1: Deletion of unnecessary checks before three function calls The functions kfree(), release_firmware() and snd_util_memhdr_free() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 2292697..b4458a6 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1289,10 +1289,8 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } if (emu->emu1010.firmware_thread) kthread_stop(emu->emu1010.firmware_thread); - if (emu->firmware) - release_firmware(emu->firmware); - if (emu->dock_fw) - release_firmware(emu->dock_fw); + release_firmware(emu->firmware); + release_firmware(emu->dock_fw); if (emu->irq >= 0) free_irq(emu->irq, emu); /* remove reserved page */ @@ -1301,8 +1299,7 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) (struct snd_util_memblk *)emu->reserved_page); emu->reserved_page = NULL; } - if (emu->memhdr) - snd_util_memhdr_free(emu->memhdr); + snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) snd_dma_free_pages(&emu->silent_page); if (emu->ptb_pages.area) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 745f062..eb5c0ab 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -777,8 +777,7 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl) kctl->private_value = 0; list_del(&ctl->list); kfree(ctl); - if (kctl->tlv.p) - kfree(kctl->tlv.p); + kfree(kctl->tlv.p); } static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, -- cgit v0.10.2 From ce5686d4ed12158599d2042a6c8659254ed263ce Mon Sep 17 00:00:00 2001 From: "Peter Zijlstra (Intel)" Date: Wed, 29 Oct 2014 11:17:04 +0100 Subject: perf/x86: Fix embarrasing typo Because we're all human and typing sucks.. Fixes: 7fb0f1de49fc ("perf/x86: Fix compile warnings for intel_uncore") Reported-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: x86@kernel.org Link: http://lkml.kernel.org/n/tip-be0bftjh8yfm4uvmvtf3yi87@git.kernel.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ded8a67..41a503c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -144,7 +144,7 @@ config INSTRUCTION_DECODER config PERF_EVENTS_INTEL_UNCORE def_bool y - depends on PERF_EVENTS && SUP_SUP_INTEL && PCI + depends on PERF_EVENTS && CPU_SUP_INTEL && PCI config OUTPUT_FORMAT string -- cgit v0.10.2 From f7b8a47da17c9ee4998f2ca2018fcc424e953c0e Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Tue, 28 Oct 2014 08:24:34 +0300 Subject: sched: Remove lockdep check in sched_move_task() sched_move_task() is the only interface to change sched_task_group: cpu_cgrp_subsys methods and autogroup_move_group() use it. Everything is synchronized by task_rq_lock(), so cpu_cgroup_attach() is ordered with other users of sched_move_task(). This means we do no need RCU here: if we've dereferenced a tg here, the .attach method hasn't been called for it yet. Thus, we should pass "true" to task_css_check() to silence lockdep warnings. Fixes: eeb61e53ea19 ("sched: Fix race between task_group and sched_task_group") Reported-by: Oleg Nesterov Reported-by: Fengguang Wu Signed-off-by: Kirill Tkhai Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Link: http://lkml.kernel.org/r/1414473874.8574.2.camel@tkhai Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 240157c..6841fb4 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7444,8 +7444,12 @@ void sched_move_task(struct task_struct *tsk) if (unlikely(running)) put_prev_task(rq, tsk); - tg = container_of(task_css_check(tsk, cpu_cgrp_id, - lockdep_is_held(&tsk->sighand->siglock)), + /* + * All callers are synchronized by task_rq_lock(); we do not use RCU + * which is pointless here. Thus, we pass "true" to task_css_check() + * to prevent lockdep warnings. + */ + tg = container_of(task_css_check(tsk, cpu_cgrp_id, true), struct task_group, css); tg = autogroup_task_group(tsk, tg); tsk->sched_task_group = tg; -- cgit v0.10.2 From 2603fe21b764eb7412598c8c6cd6199fb8b1d9c5 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Mon, 3 Nov 2014 21:35:48 +0100 Subject: ALSA: es18xx: Add GPO controls Add GPO0 and GPO1 (General Purpose Outputs) controls to mixer. These can be used on some cards to control amplifier mute (seen in ES1868 datasheet) or additional onboard chips such as QX2130 QXpander processor. These GPOs are present on ES1868, ES1869, ES1887 and ES1888 chips. Tested on ES1868 with QX2130. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 63e7323..b481bb8 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -156,6 +156,7 @@ struct snd_es18xx { #define ES18XX_I2S 0x0200 /* I2S mixer control */ #define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ #define ES18XX_CONTROL 0x0800 /* Has control ports */ +#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */ /* Power Management */ #define ES18XX_PM 0x07 @@ -1120,11 +1121,14 @@ static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg) return snd_es18xx_read(chip, reg); } -#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ +#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_es18xx_info_single, \ .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ - .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) } + +#define ES18XX_FL_INVERT (1 << 0) +#define ES18XX_FL_PMPORT (1 << 1) static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1143,10 +1147,14 @@ static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; + int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; int val; - - val = snd_es18xx_reg_read(chip, reg); + + if (pm_port) + val = inb(chip->port + ES18XX_PM); + else + val = snd_es18xx_reg_read(chip, reg); ucontrol->value.integer.value[0] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; @@ -1159,7 +1167,8 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; + int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; unsigned char val; val = (ucontrol->value.integer.value[0] & mask); @@ -1167,6 +1176,15 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e val = mask - val; mask <<= shift; val <<= shift; + if (pm_port) { + unsigned char cur = inb(chip->port + ES18XX_PM); + + if ((cur & mask) == val) + return 0; + outb((cur & ~mask) | val, chip->port + ES18XX_PM); + return 1; + } + return snd_es18xx_reg_bits(chip, reg, mask, val) != val; } @@ -1288,7 +1306,7 @@ static struct snd_kcontrol_new snd_es18xx_opt_speaker = ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0); static struct snd_kcontrol_new snd_es18xx_opt_1869[] = { -ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT), ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0), ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) @@ -1347,6 +1365,11 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = { ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), }; +static struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = { +ES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT), +ES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT), +}; + static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg) { int data; @@ -1613,10 +1636,10 @@ static int snd_es18xx_probe(struct snd_es18xx *chip, switch (chip->version) { case 0x1868: - chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL; + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT; break; case 0x1869: - chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT; break; case 0x1878: chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL; @@ -1626,7 +1649,7 @@ static int snd_es18xx_probe(struct snd_es18xx *chip, break; case 0x1887: case 0x1888: - chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME; + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT; break; default: snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n", @@ -1928,6 +1951,15 @@ static int snd_es18xx_mixer(struct snd_card *card) return err; } } + if (chip->caps & ES18XX_GPO_2BIT) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx], + chip)); + if (err < 0) + return err; + } + } return 0; } -- cgit v0.10.2 From d507941beb1ef98c19e2902007aee4faf36f854f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 4 Nov 2014 13:57:15 +0100 Subject: ALSA: pcm: Correct PCM BUG error message While converting to dev_*(), the message showing the invalid PCM position was wrongly tagged as if an XRUN although it's actually a BUG. This patch corrects the message again. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index dfc2854..7b9e2fb 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -345,7 +345,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_pcm_debug_name(substream, name, sizeof(name)); xrun_log_show(substream); pcm_err(substream->pcm, - "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, runtime->period_size); } -- cgit v0.10.2 From f5914908a5b7b2338f210e56827a1ef35585dc6d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 4 Nov 2014 12:45:59 +0100 Subject: ALSA: pcm: Replace PCM hwptr tracking with tracepoints ALSA PCM core has a mechanism tracking the PCM hwptr updates for analyzing XRUNs. But its log is limited (up to 10) and its log output is a kernel message, which is hard to handle. In this patch, the hwptr logging is moved to the tracing infrastructure instead of its own. Not only the hwptr updates but also XRUN and hwptr errors are recorded on the trace log, so that user can see such events at the exact timing. The new "snd_pcm" entry will appear in the tracing events: # ls -F /sys/kernel/debug/tracing/events/snd_pcm enable filter hw_ptr_error/ hwptr/ xrun/ The hwptr is for the regular hwptr update events. An event trace looks like: aplay-26187 [004] d..3 4012.834761: hwptr: pcmC0D0p/sub0: POS: pos=488, old=0, base=0, period=1024, buf=16384 "POS" shows the hwptr update by the explicit position update call and "IRQ" means the hwptr update by the interrupt, i.e. snd_pcm_period_elapsed() call. The "pos" is the passed ring-buffer offset by the caller, "old" is the previous hwptr, "base" is the hwptr base position, "period" and "buf" are period- and buffer-size of the target PCM substream. (Note that the hwptr position displayed here isn't the ring-buffer offset. It increments up to the PCM position boundary.) The XRUN event appears similarly, but without "pos" field. The hwptr error events appear with the PCM identifier and its reason string, such as "Lost interrupt?". The XRUN and hwptr error reports on kernel message are still left, can be turned on/off via xrun_debug proc like before. But the bit 3, 4, 5 and 6 bits of xrun_debug proc are dropped by this patch. Also, along with the change, the message strings have been reformatted to be a bit more consistent. Last but not least, the hwptr reporting is enabled only when CONFIG_SND_PCM_XRUN_DEBUG is set. Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt index 7fcd1ad..cfc4956 100644 --- a/Documentation/sound/alsa/Procfile.txt +++ b/Documentation/sound/alsa/Procfile.txt @@ -101,10 +101,6 @@ card*/pcm*/xrun_debug bit 0 = Enable XRUN/jiffies debug messages bit 1 = Show stack trace at XRUN / jiffies check bit 2 = Enable additional jiffies check - bit 3 = Log hwptr update at each period interrupt - bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr() - bit 5 = Show last 10 positions on error - bit 6 = Do above only once When the bit 0 is set, the driver will show the messages to kernel log when an xrun is detected. The debug message is @@ -121,15 +117,6 @@ card*/pcm*/xrun_debug buggy) hardware that doesn't give smooth pointer updates. This feature is enabled via the bit 2. - Bits 3 and 4 are for logging the hwptr records. Note that - these will give flood of kernel messages. - - When bit 5 is set, the driver logs the last 10 xrun errors and - the proc file shows each jiffies, position, period_size, - buffer_size, old_hw_ptr, and hw_ptr_base values. - - When bit 6 is set, the full xrun log is shown only once. - card*/pcm*/sub*/info The general information of this PCM sub-stream. diff --git a/sound/core/Makefile b/sound/core/Makefile index 394a389..4daf2f5 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o +# for trace-points +CFLAGS_pcm_lib.o := -I$(src) + snd-pcm-dmaengine-objs := pcm_dmaengine.o snd-rawmidi-objs := rawmidi.o diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7b9e2fb..ec9e786 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -32,6 +32,15 @@ #include #include +#ifdef CONFIG_SND_PCM_XRUN_DEBUG +#define CREATE_TRACE_POINTS +#include "pcm_trace.h" +#else +#define trace_hwptr(substream, pos, in_interrupt) +#define trace_xrun(substream) +#define trace_hw_ptr_error(substream, reason) +#endif + /* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area @@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name); #define XRUN_DEBUG_BASIC (1<<0) #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ -#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ -#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ -#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ -#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + trace_xrun(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); @@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream) } #ifdef CONFIG_SND_PCM_XRUN_DEBUG -#define hw_ptr_error(substream, fmt, args...) \ +#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \ do { \ + trace_hw_ptr_error(substream, reason); \ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ - xrun_log_show(substream); \ - pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \ + pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \ + (in_interrupt) ? 'Q' : 'P', ##args); \ dump_stack_on_xrun(substream); \ } \ } while (0) -#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; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t old_hw_ptr; - snd_pcm_uframes_t hw_ptr_base; -}; - -struct snd_pcm_hwptr_log { - unsigned int idx; - unsigned int hit: 1; - struct hwptr_log_entry entries[XRUN_LOG_CNT]; -}; - -static void xrun_log(struct snd_pcm_substream *substream, - snd_pcm_uframes_t pos, int in_interrupt) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hwptr_log *log = runtime->hwptr_log; - struct hwptr_log_entry *entry; - - if (log == NULL) { - log = kzalloc(sizeof(*log), GFP_ATOMIC); - if (log == NULL) - return; - runtime->hwptr_log = log; - } else { - if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) - return; - } - entry = &log->entries[log->idx]; - entry->in_interrupt = in_interrupt; - entry->jiffies = jiffies; - entry->pos = pos; - entry->period_size = runtime->period_size; - entry->buffer_size = runtime->buffer_size; - entry->old_hw_ptr = runtime->status->hw_ptr; - entry->hw_ptr_base = runtime->hw_ptr_base; - log->idx = (log->idx + 1) % XRUN_LOG_CNT; -} - -static void xrun_log_show(struct snd_pcm_substream *substream) -{ - struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; - struct hwptr_log_entry *entry; - char name[16]; - unsigned int idx; - int cnt; - - if (log == NULL) - return; - if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) - return; - snd_pcm_debug_name(substream, name, sizeof(name)); - for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { - entry = &log->entries[idx]; - if (entry->period_size == 0) - break; - pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " - "hwptr=%ld/%ld\n", - 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, - (unsigned long)entry->hw_ptr_base); - idx++; - idx %= XRUN_LOG_CNT; - } - log->hit = 1; -} - #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ #define hw_ptr_error(substream, fmt, args...) do { } while (0) -#define xrun_log(substream, pos, in_interrupt) do { } while (0) -#define xrun_log_show(substream) do { } while (0) #endif @@ -343,7 +271,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (printk_ratelimit()) { char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - xrun_log_show(substream); pcm_err(substream->pcm, "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, @@ -352,8 +279,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, pos = 0; } pos -= pos % runtime->min_align; - if (xrun_debug(substream, XRUN_DEBUG_LOG)) - xrun_log(substream, pos, in_interrupt); + trace_hwptr(substream, pos, in_interrupt); hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; if (in_interrupt) { @@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, delta = new_hw_ptr - old_hw_ptr; if (delta < 0) delta += runtime->boundary; - if (xrun_debug(substream, in_interrupt ? - XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { - char name[16]; - snd_pcm_debug_name(substream, name, sizeof(name)); - pcm_dbg(substream->pcm, - "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n", - in_interrupt ? "period" : "hwptr", - name, - (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)delta, - (unsigned long)old_hw_ptr, - (unsigned long)new_hw_ptr, - (unsigned long)runtime->hw_ptr_base); - } if (runtime->no_period_wakeup) { snd_pcm_sframes_t xrun_threshold; @@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, /* something must be really wrong */ if (delta >= runtime->buffer_size + runtime->period_size) { - hw_ptr_error(substream, - "Unexpected hw_pointer value %s" - "(stream=%i, pos=%ld, new_hw_ptr=%ld, " - "old_hw_ptr=%ld)\n", - in_interrupt ? "[Q] " : "[P]", - substream->stream, (long)pos, - (long)new_hw_ptr, (long)old_hw_ptr); + hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", + "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", + substream->stream, (long)pos, + (long)new_hw_ptr, (long)old_hw_ptr); return 0; } @@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, delta--; } /* align hw_base to buffer_size */ - hw_ptr_error(substream, - "hw_ptr skipping! %s" - "(pos=%ld, delta=%ld, period=%ld, " - "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", - in_interrupt ? "[Q] " : "", + hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", + "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", (long)pos, (long)hdelta, (long)runtime->period_size, jdelta, ((hdelta * HZ) / runtime->rate), hw_base, @@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { - hw_ptr_error(substream, - "Lost interrupts? %s" - "(stream=%i, delta=%ld, new_hw_ptr=%ld, " - "old_hw_ptr=%ld)\n", - in_interrupt ? "[Q] " : "", + hw_ptr_error(substream, in_interrupt, + "Lost interrupts?", + "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", substream->stream, (long)delta, (long)new_hw_ptr, (long)old_hw_ptr); diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h new file mode 100644 index 0000000..b63b654 --- /dev/null +++ b/sound/core/pcm_trace.h @@ -0,0 +1,110 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_pcm +#define TRACE_INCLUDE_FILE pcm_trace + +#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _PCM_TRACE_H + +#include + +TRACE_EVENT(hwptr, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq), + TP_ARGS(substream, pos, irq), + TP_STRUCT__entry( + __field( bool, in_interrupt ) + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, pos ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + __field( snd_pcm_uframes_t, old_hw_ptr ) + __field( snd_pcm_uframes_t, hw_ptr_base ) + ), + TP_fast_assign( + __entry->in_interrupt = (irq); + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->pos = (pos); + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; + __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; + ), + TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, + __entry->in_interrupt ? "IRQ" : "POS", + (unsigned long)__entry->pos, + (unsigned long)__entry->old_hw_ptr, + (unsigned long)__entry->hw_ptr_base, + (unsigned long)__entry->period_size, + (unsigned long)__entry->buffer_size) +); + +TRACE_EVENT(xrun, + TP_PROTO(struct snd_pcm_substream *substream), + TP_ARGS(substream), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + __field( snd_pcm_uframes_t, old_hw_ptr ) + __field( snd_pcm_uframes_t, hw_ptr_base ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; + __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; + ), + TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, + (unsigned long)__entry->old_hw_ptr, + (unsigned long)__entry->hw_ptr_base, + (unsigned long)__entry->period_size, + (unsigned long)__entry->buffer_size) +); + +TRACE_EVENT(hw_ptr_error, + TP_PROTO(struct snd_pcm_substream *substream, const char *why), + TP_ARGS(substream, why), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( const char *, reason ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->reason = (why); + ), + TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, __entry->reason) +); + +#endif /* _PCM_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include -- cgit v0.10.2 From 2b30d411dbc6eddfb5b4f9afd5a2c57b6f4dd96c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 4 Nov 2014 14:02:40 +0100 Subject: ALSA: pcm: Add xrun_injection proc entry This patch adds a new proc entry for PCM substreams to inject an XRUN. When a PCM substream is running and any value is written to its xrun_injection proc file, the driver triggers XRUN. This is a useful feature for debugging XRUN and error handling code paths. Note that this entry is enabled only when CONFIG_SND_PCM_XRUN_DEBUG is set. Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt index cfc4956..7f8a0d3 100644 --- a/Documentation/sound/alsa/Procfile.txt +++ b/Documentation/sound/alsa/Procfile.txt @@ -133,6 +133,10 @@ card*/pcm*/sub*/sw_params card*/pcm*/sub*/prealloc The buffer pre-allocation information. +card*/pcm*/sub*/xrun_injection + Triggers an XRUN to the running stream when any value is + written to this proc file. Used for fault injection. + This entry is write-only. AC97 Codec Information ---------------------- diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 29eb09e..0b8daee 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -416,7 +416,10 @@ struct snd_pcm_substream { struct snd_info_entry *proc_status_entry; struct snd_info_entry *proc_prealloc_entry; struct snd_info_entry *proc_prealloc_max_entry; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + struct snd_info_entry *proc_xrun_injection_entry; #endif +#endif /* CONFIG_SND_VERBOSE_PROCFS */ /* misc flags */ unsigned int hw_opened: 1; }; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 31acc3d..8f624b7 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -483,6 +483,19 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, } #ifdef CONFIG_SND_PCM_XRUN_DEBUG +static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_pcm_substream *substream = entry->private_data; + struct snd_pcm_runtime *runtime; + + snd_pcm_stream_lock_irq(substream); + runtime = substream->runtime; + if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); +} + static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -614,6 +627,22 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) } substream->proc_status_entry = entry; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + entry = snd_info_create_card_entry(card, "xrun_injection", + substream->proc_root); + if (entry) { + entry->private_data = substream; + entry->c.text.read = NULL; + entry->c.text.write = snd_pcm_xrun_injection_write; + entry->mode = S_IFREG | S_IWUSR; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_xrun_injection_entry = entry; +#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ + return 0; } @@ -627,6 +656,10 @@ static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) substream->proc_sw_params_entry = NULL; snd_info_free_entry(substream->proc_status_entry); substream->proc_status_entry = NULL; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + snd_info_free_entry(substream->proc_xrun_injection_entry); + substream->proc_xrun_injection_entry = NULL; +#endif snd_info_free_entry(substream->proc_root); substream->proc_root = NULL; return 0; -- cgit v0.10.2 From 4c8c3a4fcc021677c9a363b4e77f61dd09dbfd1a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:00:23 +0100 Subject: ALSA: usb-audio: Flatten probe and disconnect functions The usb-audio probe and disconnect functions have been split just for adapting the (new!) API at 2.5 kernel time. We left them until now, partly because we wanted to build with the pretty old kernels in the external alsa-driver tree. But the support of such old kernels has been longly stopped, so it's good time to clean up this mess. One good point by this cleanup is that now the probe function returns a proper error code instead of only -EIO. Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index 7ecd0e8..be16bdc 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -112,7 +112,7 @@ static struct usb_driver usb_audio_driver; /* * disconnect streams - * called from snd_usb_audio_disconnect() + * called from usb_audio_disconnect() */ static void snd_usb_stream_disconnect(struct list_head *head) { @@ -475,14 +475,14 @@ static int snd_usb_audio_create(struct usb_interface *intf, * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -static struct snd_usb_audio * -snd_usb_audio_probe(struct usb_device *dev, - struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) { - const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; - int i, err; + struct usb_device *dev = interface_to_usbdev(intf); + const struct snd_usb_audio_quirk *quirk = + (const struct snd_usb_audio_quirk *)usb_id->driver_info; struct snd_usb_audio *chip; + int i, err; struct usb_host_interface *alts; int ifnum; u32 id; @@ -492,10 +492,11 @@ snd_usb_audio_probe(struct usb_device *dev, id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) - goto __err_val; + return -ENXIO; - if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0) - goto __err_val; + err = snd_usb_apply_boot_quirk(dev, intf, quirk); + if (err < 0) + return err; /* * found a config. now register to ALSA @@ -508,6 +509,7 @@ snd_usb_audio_probe(struct usb_device *dev, if (usb_chip[i] && usb_chip[i]->dev == dev) { if (usb_chip[i]->shutdown) { dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); + err = -EIO; goto __error; } chip = usb_chip[i]; @@ -523,15 +525,16 @@ snd_usb_audio_probe(struct usb_device *dev, if (enable[i] && ! usb_chip[i] && (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { - if (snd_usb_audio_create(intf, dev, i, quirk, - &chip) < 0) { + err = snd_usb_audio_create(intf, dev, i, quirk, + &chip); + if (err < 0) goto __error; - } chip->pm_intf = intf; break; } if (!chip) { dev_err(&dev->dev, "no available usb audio device\n"); + err = -ENODEV; goto __error; } } @@ -548,28 +551,32 @@ snd_usb_audio_probe(struct usb_device *dev, err = 1; /* continue */ if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { /* need some special handlings */ - if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) + err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk); + if (err < 0) goto __error; } if (err > 0) { /* create normal USB audio interfaces */ - if (snd_usb_create_streams(chip, ifnum) < 0 || - snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) { + err = snd_usb_create_streams(chip, ifnum); + if (err < 0) + goto __error; + err = snd_usb_create_mixer(chip, ifnum, ignore_ctl_error); + if (err < 0) goto __error; - } } /* we are allowed to call snd_card_register() many times */ - if (snd_card_register(chip->card) < 0) { + err = snd_card_register(chip->card); + if (err < 0) goto __error; - } usb_chip[chip->index] = chip; chip->num_interfaces++; chip->probing = 0; + usb_set_intfdata(intf, chip); mutex_unlock(®ister_mutex); - return chip; + return 0; __error: if (chip) { @@ -578,17 +585,16 @@ snd_usb_audio_probe(struct usb_device *dev, chip->probing = 0; } mutex_unlock(®ister_mutex); - __err_val: - return NULL; + return err; } /* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -static void snd_usb_audio_disconnect(struct usb_device *dev, - struct snd_usb_audio *chip) +static void usb_audio_disconnect(struct usb_interface *intf) { + struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_card *card; struct list_head *p; @@ -630,27 +636,6 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, } } -/* - * new 2.5 USB kernel API - */ -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct snd_usb_audio *chip; - chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); - if (chip) { - usb_set_intfdata(intf, chip); - return 0; - } else - return -EIO; -} - -static void usb_audio_disconnect(struct usb_interface *intf) -{ - snd_usb_audio_disconnect(interface_to_usbdev(intf), - usb_get_intfdata(intf)); -} - #ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip) -- cgit v0.10.2 From a6cece9d81990e729c1f9da2a5bff2d29f7df649 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:24:32 +0100 Subject: ALSA: usb-audio: Pass direct struct pointer instead of list_head Some functions in mixer.c and endpoint.c receive list_head instead of the object itself. This is not obvious and rather error-prone. Let's pass the proper object directly instead. The functions in midi.c still receive list_head and this can't be changed since the object definition isn't exposed to the outside of midi.c, so left as is. Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index be16bdc..fa6c097 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -114,13 +114,11 @@ static struct usb_driver usb_audio_driver; * disconnect streams * called from usb_audio_disconnect() */ -static void snd_usb_stream_disconnect(struct list_head *head) +static void snd_usb_stream_disconnect(struct snd_usb_stream *as) { int idx; - struct snd_usb_stream *as; struct snd_usb_substream *subs; - as = list_entry(head, struct snd_usb_stream, list); for (idx = 0; idx < 2; idx++) { subs = &as->substream[idx]; if (!subs->num_formats) @@ -307,10 +305,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) static int snd_usb_audio_free(struct snd_usb_audio *chip) { - struct list_head *p, *n; + struct snd_usb_endpoint *ep, *n; - list_for_each_safe(p, n, &chip->ep_list) - snd_usb_endpoint_free(p); + list_for_each_entry_safe(ep, n, &chip->ep_list, list) + snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); kfree(chip); @@ -609,12 +607,14 @@ static void usb_audio_disconnect(struct usb_interface *intf) mutex_lock(®ister_mutex); chip->num_interfaces--; if (chip->num_interfaces <= 0) { + struct snd_usb_stream *as; struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; snd_card_disconnect(card); /* release the pcm resources */ - list_for_each(p, &chip->pcm_list) { - snd_usb_stream_disconnect(p); + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); } /* release the endpoint resources */ list_for_each_entry(ep, &chip->ep_list, list) { @@ -625,8 +625,8 @@ static void usb_audio_disconnect(struct usb_interface *intf) snd_usbmidi_disconnect(p); } /* release mixer resources */ - list_for_each(p, &chip->mixer_list) { - snd_usb_mixer_disconnect(p); + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); } usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 114e3e7..167d0c1 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1002,15 +1002,12 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep) /** * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint * - * @ep: the list header of the endpoint to free + * @ep: the endpoint to free * * This free all resources of the given ep. */ -void snd_usb_endpoint_free(struct list_head *head) +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) { - struct snd_usb_endpoint *ep; - - ep = list_entry(head, struct snd_usb_endpoint, list); kfree(ep); } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e61ee5c..6428392 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -24,7 +24,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); -void snd_usb_endpoint_free(struct list_head *head); +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 63a8adb..e4aaa21 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2483,11 +2483,8 @@ _error: return err; } -void snd_usb_mixer_disconnect(struct list_head *p) +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) { - struct usb_mixer_interface *mixer; - - mixer = list_entry(p, struct usb_mixer_interface, list); usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb); } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 73b1f64..2c7b9c9 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -57,7 +57,7 @@ struct usb_mixer_elem_info { int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error); -void snd_usb_mixer_disconnect(struct list_head *p); +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer); void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); -- cgit v0.10.2 From ae366c2049b48b54e5250cb57438bbebd1dbe610 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 31 Oct 2014 11:32:19 +0100 Subject: ALSA: usb-audio: Use strim() instead of open code Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index fa6c097..69725d5 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -321,16 +321,6 @@ static int snd_usb_audio_dev_free(struct snd_device *device) return snd_usb_audio_free(chip); } -static void remove_trailing_spaces(char *str) -{ - char *p; - - if (!*str) - return; - for (p = str + strlen(str) - 1; p >= str && isspace(*p); p--) - *p = 0; -} - /* * create a chip instance and set its names. */ @@ -414,7 +404,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, USB_ID_PRODUCT(chip->usb_id)); } } - remove_trailing_spaces(card->shortname); + strim(card->shortname); /* retrieve the vendor and device strings as longname */ if (quirk && quirk->vendor_name && *quirk->vendor_name) { @@ -428,7 +418,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, /* we don't really care if there isn't any vendor string */ } if (len > 0) { - remove_trailing_spaces(card->longname); + strim(card->longname); if (*card->longname) strlcat(card->longname, " ", sizeof(card->longname)); } -- cgit v0.10.2 From 9161bd0d1cf375492f0a6aa86b3e4c28b070fb7c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 5 Nov 2014 19:51:56 +0530 Subject: ALSA: echoaudio: cleanup of unnecessary messages commit "b5b4a41b392960010fccf1f9ccf8334d612bd450" was dereferencing chip after it has been freed. This patch fixes that and at the same time removes some debugging messages, which are unnecessary, as they are just printing information about entry and exit from a function, and which switch-case it is executing. we can easily get from ftrace the information about the entry and exit from a function. Reported-by: Dan Carpenter Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index c94e92e..febee5b 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -33,7 +33,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Darla20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20)) return -ENODEV; @@ -58,7 +57,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw: done\n"); return err; } diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index b1272f88..7b4f6fd 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -33,7 +33,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Darla24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24)) return -ENODEV; @@ -57,7 +56,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw: done\n"); return err; } diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index bc37168..ae11ce1 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -46,7 +46,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) int err; local_irq_enable(); - dev_dbg(chip->card->dev, "init_hw() - Echo3G\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G)) return -ENODEV; @@ -99,7 +98,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 1ef29e5..60e4003 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -79,7 +79,6 @@ static void free_firmware(const struct firmware *fw_entry, dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); #else release_firmware(fw_entry); - dev_dbg(chip->card->dev, "firmware released\n"); #endif } @@ -96,7 +95,6 @@ static void free_firmware_cache(struct echoaudio *chip) dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - dev_dbg(chip->card->dev, "firmware_cache released\n"); #endif } @@ -354,7 +352,6 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err; - dev_dbg(chip->card->dev, "pcm_analog_in_open\n"); if ((err = pcm_open(substream, num_analog_busses_in(chip) - substream->number)) < 0) return err; @@ -389,7 +386,6 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream) #else max_channels = num_analog_busses_out(chip); #endif - dev_dbg(chip->card->dev, "pcm_analog_out_open\n"); if ((err = pcm_open(substream, max_channels - substream->number)) < 0) return err; if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, @@ -422,7 +418,6 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - dev_dbg(chip->card->dev, "pcm_digital_in_open\n"); max_channels = num_digital_busses_in(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -464,7 +459,6 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - dev_dbg(chip->card->dev, "pcm_digital_out_open\n"); max_channels = num_digital_busses_out(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -511,7 +505,6 @@ static int pcm_close(struct snd_pcm_substream *substream) /* Nothing to do here. Audio is already off and pipe will be * freed by its callback */ - dev_dbg(chip->card->dev, "pcm_close\n"); atomic_dec(&chip->opencount); oc = atomic_read(&chip->opencount); @@ -620,7 +613,6 @@ static int init_engine(struct snd_pcm_substream *substream, spin_lock_irq(&chip->lock); set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); spin_unlock_irq(&chip->lock); - dev_dbg(chip->card->dev, "pcm_hw_params ok\n"); return 0; } @@ -691,7 +683,6 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) } spin_unlock_irq(&chip->lock); - dev_dbg(chip->card->dev, "pcm_hw_freed\n"); snd_pcm_lib_free_pages(substream); return 0; } @@ -763,10 +754,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - dev_dbg(chip->card->dev, "pcm_trigger resume\n"); case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dev_dbg(chip->card->dev, "pcm_trigger start\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -788,9 +777,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) chip->pipe_cyclic_mask); break; case SNDRV_PCM_TRIGGER_SUSPEND: - dev_dbg(chip->card->dev, "pcm_trigger suspend\n"); case SNDRV_PCM_TRIGGER_STOP: - dev_dbg(chip->card->dev, "pcm_trigger stop\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -800,7 +787,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) err = stop_transport(chip, channelmask); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dev_dbg(chip->card->dev, "pcm_trigger pause\n"); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -937,7 +923,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - dev_dbg(chip->card->dev, "Analog PCM ok\n"); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital inputs, no outputs */ @@ -950,7 +935,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - dev_dbg(chip->card->dev, "Digital PCM ok\n"); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #else /* ECHOCARD_HAS_VMIXER */ @@ -972,7 +956,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - dev_dbg(chip->card->dev, "Analog PCM ok\n"); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital i/o */ @@ -987,7 +970,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - dev_dbg(chip->card->dev, "Digital PCM ok\n"); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #endif /* ECHOCARD_HAS_VMIXER */ @@ -1881,10 +1863,8 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) static int snd_echo_free(struct echoaudio *chip) { - dev_dbg(chip->card->dev, "Stop DSP...\n"); if (chip->comm_page) rest_in_peace(chip); - dev_dbg(chip->card->dev, "Stopped.\n"); if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -1898,14 +1878,12 @@ static int snd_echo_free(struct echoaudio *chip) if (chip->iores) release_and_free_resource(chip->iores); - dev_dbg(chip->card->dev, "MMIO freed.\n"); pci_disable_device(chip->pci); /* release chip data */ free_firmware_cache(chip); kfree(chip); - dev_dbg(chip->card->dev, "Chip freed.\n"); return 0; } @@ -1915,7 +1893,6 @@ static int snd_echo_dev_free(struct snd_device *device) { struct echoaudio *chip = device->device_data; - dev_dbg(chip->card->dev, "snd_echo_dev_free()...\n"); return snd_echo_free(chip); } @@ -2008,7 +1985,6 @@ static int snd_echo_create(struct snd_card *card, snd_echo_free(chip); return err; } - dev_dbg(card->dev, "Card init OK\n"); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_echo_free(chip); @@ -2038,7 +2014,6 @@ static int snd_echo_probe(struct pci_dev *pci, return -ENOENT; } - dev_dbg(&pci->dev, "Echoaudio driver starting...\n"); i = 0; err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); @@ -2191,7 +2166,6 @@ static int snd_echo_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct echoaudio *chip = dev_get_drvdata(dev); - dev_dbg(dev, "suspend start\n"); snd_pcm_suspend_all(chip->analog_pcm); snd_pcm_suspend_all(chip->digital_pcm); @@ -2218,7 +2192,6 @@ static int snd_echo_suspend(struct device *dev) pci_save_state(pci); pci_disable_device(pci); - dev_dbg(dev, "suspend done\n"); return 0; } @@ -2232,7 +2205,6 @@ static int snd_echo_resume(struct device *dev) u32 pipe_alloc_mask; int err; - dev_dbg(dev, "resume start\n"); pci_restore_state(pci); commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL); if (commpage_bak == NULL) @@ -2247,7 +2219,6 @@ static int snd_echo_resume(struct device *dev) snd_echo_free(chip); return err; } - dev_dbg(dev, "resume init OK\n"); /* Temporarily set chip->pipe_alloc_mask=0 otherwise * restore_dsp_settings() fails. @@ -2260,7 +2231,6 @@ static int snd_echo_resume(struct device *dev) kfree(commpage_bak); return err; } - dev_dbg(dev, "resume restore OK\n"); memcpy(&commpage->audio_format, &commpage_bak->audio_format, sizeof(commpage->audio_format)); @@ -2286,7 +2256,6 @@ static int snd_echo_resume(struct device *dev) snd_echo_midi_output_trigger(chip->midi_out, 1); #endif - dev_dbg(dev, "resume done\n"); return 0; } diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index 50a2169..2fa66dc 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -328,7 +328,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -337,13 +336,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - dev_dbg(chip->card->dev, "Set Echo3G clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - dev_dbg(chip->card->dev, "Set Echo3G clock to SPDIF\n"); control_reg |= E3G_SPDIF_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96) control_reg |= E3G_DOUBLE_SPEED_MODE; @@ -353,12 +350,10 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - dev_dbg(chip->card->dev, "Set Echo3G clock to ADAT\n"); control_reg |= E3G_ADAT_CLOCK; control_reg &= ~E3G_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - dev_dbg(chip->card->dev, "Set Echo3G clock to WORD\n"); control_reg |= E3G_WORD_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96) control_reg |= E3G_DOUBLE_SPEED_MODE; diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index ba9d4f1..1a9427a 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -206,7 +206,6 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) goto la_error; } - dev_dbg(chip->card->dev, "ASIC loaded\n"); free_firmware(fw, chip); return 0; @@ -473,7 +472,6 @@ static int load_dsp(struct echoaudio *chip, u16 *code) chip->dsp_code = code; /* Show which DSP code loaded */ chip->bad_board = FALSE; /* DSP OK */ - dev_dbg(chip->card->dev, "load_dsp: OK!\n"); return 0; } udelay(100); @@ -674,7 +672,6 @@ static void get_audio_meters(struct echoaudio *chip, long *meters) static int restore_dsp_rettings(struct echoaudio *chip) { int i, o, err; - dev_dbg(chip->card->dev, "restore_dsp_settings\n"); if ((err = check_asic_status(chip)) < 0) return err; @@ -771,7 +768,6 @@ static int restore_dsp_rettings(struct echoaudio *chip) if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0) return -EIO; - dev_dbg(chip->card->dev, "restore_dsp_rettings done\n"); return 0; } @@ -865,7 +861,6 @@ Same thing for pause_ and stop_ -trasport below. */ static int start_transport(struct echoaudio *chip, u32 channel_mask, u32 cyclic_mask) { - dev_dbg(chip->card->dev, "start_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -891,7 +886,6 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask, static int pause_transport(struct echoaudio *chip, u32 channel_mask) { - dev_dbg(chip->card->dev, "pause_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -918,7 +912,6 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask) static int stop_transport(struct echoaudio *chip, u32 channel_mask) { - dev_dbg(chip->card->dev, "stop_transport %x\n", channel_mask); if (wait_handshake(chip)) return -EIO; @@ -954,8 +947,6 @@ static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) stopped and unallocated. */ static int rest_in_peace(struct echoaudio *chip) { - dev_dbg(chip->card->dev, - "rest_in_peace() open=%x\n", chip->pipe_alloc_mask); /* Stops all active pipes (just to be sure) */ stop_transport(chip, chip->active_mask); @@ -1018,7 +1009,6 @@ static int init_dsp_comm_page(struct echoaudio *chip) */ static int init_line_levels(struct echoaudio *chip) { - dev_dbg(chip->card->dev, "init_line_levels\n"); memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain)); memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain)); memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain)); @@ -1099,7 +1089,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, it moves data. The DMA counter is in units of bytes, not samples. */ pipe->dma_counter = &chip->comm_page->position[pipe_index]; *pipe->dma_counter = 0; - dev_dbg(chip->card->dev, "allocate_pipes: ok\n"); return pipe_index; } @@ -1110,7 +1099,6 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) u32 channel_mask; int i; - dev_dbg(chip->card->dev, "free_pipes: Pipe %d\n", pipe->index); if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index))) return -EINVAL; if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED)) diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index a959eae..5dafe92 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -37,7 +37,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Gina20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20)) return -ENODEV; @@ -63,7 +62,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -150,7 +148,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) static int set_input_clock(struct echoaudio *chip, u16 clock) { - dev_dbg(chip->card->dev, "set_input_clock:\n"); switch (clock) { case ECHO_CLOCK_INTERNAL: @@ -159,7 +156,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) chip->spdif_status = GD_SPDIF_STATUS_UNDEF; set_sample_rate(chip, chip->sample_rate); chip->input_clock = clock; - dev_dbg(chip->card->dev, "Set Gina clock to INTERNAL\n"); break; case ECHO_CLOCK_SPDIF: chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN; @@ -167,7 +163,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) clear_handshake(chip); send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); chip->clock_state = GD_CLOCK_SPDIFIN; - dev_dbg(chip->card->dev, "Set Gina20 clock to SPDIF\n"); chip->input_clock = clock; break; default: @@ -209,7 +204,6 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index c8ea576..6971766 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -41,7 +41,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Gina24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24)) return -ENODEV; @@ -79,7 +78,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -156,7 +154,6 @@ static int load_asic(struct echoaudio *chip) control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; err = write_control_reg(chip, control_reg, TRUE); } - dev_dbg(chip->card->dev, "load_asic() done\n"); return err; } @@ -238,7 +235,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -247,13 +243,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - dev_dbg(chip->card->dev, "Set Gina24 clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - dev_dbg(chip->card->dev, "Set Gina24 clock to SPDIF\n"); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -263,17 +257,14 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - dev_dbg(chip->card->dev, "Set Gina24 clock to ADAT\n"); control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC: - dev_dbg(chip->card->dev, "Set Gina24 clock to ESYNC\n"); control_reg |= GML_ESYNC_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC96: - dev_dbg(chip->card->dev, "Set Gina24 clock to ESYNC96\n"); control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE; break; default: diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index cdeb073..54edd67 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -38,7 +38,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Indigo\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO)) return -ENODEV; @@ -61,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 133915c..2cf5cc0 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -38,7 +38,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Indigo DJ\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ)) return -ENODEV; @@ -61,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index 26cdfcf..5252863 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -35,7 +35,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Indigo DJx\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX)) return -ENODEV; @@ -60,7 +59,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index 5e6df7c..4e81787 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -38,7 +38,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Indigo IO\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO)) return -ENODEV; @@ -61,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index 90cdd27..6de3f9b 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -35,7 +35,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Indigo IOx\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX)) return -ENODEV; @@ -60,7 +59,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 7f0f6ea..f2024a3 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -40,7 +40,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Layla20\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) return -ENODEV; @@ -65,7 +64,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -190,24 +188,19 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) u16 clock; u32 rate; - dev_dbg(chip->card->dev, "set_input_clock:\n"); rate = 0; switch (clock_source) { case ECHO_CLOCK_INTERNAL: - dev_dbg(chip->card->dev, "Set Layla20 clock to INTERNAL\n"); rate = chip->sample_rate; clock = LAYLA20_CLOCK_INTERNAL; break; case ECHO_CLOCK_SPDIF: - dev_dbg(chip->card->dev, "Set Layla20 clock to SPDIF\n"); clock = LAYLA20_CLOCK_SPDIF; break; case ECHO_CLOCK_WORD: - dev_dbg(chip->card->dev, "Set Layla20 clock to WORD\n"); clock = LAYLA20_CLOCK_WORD; break; case ECHO_CLOCK_SUPER: - dev_dbg(chip->card->dev, "Set Layla20 clock to SUPER\n"); clock = LAYLA20_CLOCK_SUPER; break; default: @@ -232,7 +225,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) static int set_output_clock(struct echoaudio *chip, u16 clock) { - dev_dbg(chip->card->dev, "set_output_clock: %d\n", clock); switch (clock) { case ECHO_CLOCK_SUPER: clock = LAYLA20_OUTPUT_CLOCK_SUPER; @@ -286,7 +278,6 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index eb8f218..4f11e81 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -40,7 +40,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Layla24\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24)) return -ENODEV; @@ -70,7 +69,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = init_line_levels(chip)) < 0) return err; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -118,7 +116,6 @@ static int load_asic(struct echoaudio *chip) if (chip->asic_loaded) return 1; - dev_dbg(chip->card->dev, "load_asic\n"); /* Give the DSP a few milliseconds to settle down */ mdelay(10); @@ -152,7 +149,6 @@ static int load_asic(struct echoaudio *chip) err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, TRUE); - dev_dbg(chip->card->dev, "load_asic() done\n"); return err; } @@ -262,7 +258,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) /* Pick the new clock */ switch (clock) { case ECHO_CLOCK_INTERNAL: - dev_dbg(chip->card->dev, "Set Layla24 clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -271,7 +266,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_SPDIF_CLOCK; /* Layla24 doesn't support 96KHz S/PDIF */ control_reg &= ~GML_DOUBLE_SPEED_MODE; - dev_dbg(chip->card->dev, "Set Layla24 clock to SPDIF\n"); break; case ECHO_CLOCK_WORD: control_reg |= GML_WORD_CLOCK; @@ -279,14 +273,12 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_DOUBLE_SPEED_MODE; else control_reg &= ~GML_DOUBLE_SPEED_MODE; - dev_dbg(chip->card->dev, "Set Layla24 clock to WORD\n"); break; case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; - dev_dbg(chip->card->dev, "Set Layla24 clock to ADAT\n"); break; default: dev_err(chip->card->dev, diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index ed2f21d..fdad079 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -41,7 +41,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Mia\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA)) return -ENODEV; @@ -67,7 +66,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index 8d43c5a..d913749 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -157,7 +157,6 @@ static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = substream; - dev_dbg(chip->card->dev, "rawmidi_iopen\n"); return 0; } @@ -183,7 +182,6 @@ static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = NULL; - dev_dbg(chip->card->dev, "rawmidi_iclose\n"); return 0; } @@ -196,7 +194,6 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) chip->tinuse = 0; chip->midi_full = 0; chip->midi_out = substream; - dev_dbg(chip->card->dev, "rawmidi_open\n"); return 0; } @@ -209,7 +206,6 @@ static void snd_echo_midi_output_write(unsigned long data) int bytes, sent, time; unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; - dev_dbg(chip->card->dev, "snd_echo_midi_output_write\n"); /* No interrupts are involved: we have to check at regular intervals if the card's output buffer has room for new data. */ sent = bytes = 0; @@ -288,7 +284,6 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_out = NULL; - dev_dbg(chip->card->dev, "rawmidi_oclose\n"); return 0; } @@ -328,6 +323,5 @@ static int snd_echo_midi_create(struct snd_card *card, chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - dev_dbg(chip->card->dev, "MIDI ok\n"); return 0; } diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index cc46a8c..843c7a5 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -41,7 +41,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - dev_dbg(chip->card->dev, "init_hw() - Mona\n"); if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA)) return -ENODEV; @@ -72,7 +71,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - dev_dbg(chip->card->dev, "init_hw done\n"); return err; } @@ -302,7 +300,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) u32 control_reg, clocks_from_dsp; int err; - dev_dbg(chip->card->dev, "set_input_clock:\n"); /* Prevent two simultaneous calls to switch_asic() */ if (atomic_read(&chip->opencount)) @@ -315,7 +312,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - dev_dbg(chip->card->dev, "Set Mona clock to INTERNAL\n"); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -327,7 +323,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) spin_lock_irq(&chip->lock); if (err < 0) return err; - dev_dbg(chip->card->dev, "Set Mona clock to SPDIF\n"); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -335,7 +330,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - dev_dbg(chip->card->dev, "Set Mona clock to WORD\n"); spin_unlock_irq(&chip->lock); err = switch_asic(chip, clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96); -- cgit v0.10.2 From 9bc889b4ba88a3f2d81e4b799d47d71d7381573a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Nov 2014 12:15:25 +0100 Subject: ALSA: pcm: Update the state properly before notification Some state changes (e.g. snd_pcm_stop()) sets the runtime state after calling snd_timer_notify(). This is basically racy, since the notification may wakes up the user even before the state change. Although the possibility is low, we should set the state before the notifications. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ca224fa..dfb5031 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1052,10 +1052,10 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->status->state != state) { snd_pcm_trigger_tstamp(substream); + runtime->status->state = state; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); - runtime->status->state = state; } wake_up(&runtime->sleep); wake_up(&runtime->tsleep); @@ -1204,11 +1204,11 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); + runtime->status->suspended_state = runtime->status->state; + runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, &runtime->trigger_tstamp); - runtime->status->suspended_state = runtime->status->state; - runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; wake_up(&runtime->sleep); wake_up(&runtime->tsleep); } @@ -1311,10 +1311,10 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); + runtime->status->state = runtime->status->suspended_state; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp); - runtime->status->state = runtime->status->suspended_state; } static struct action_ops snd_pcm_action_resume = { -- cgit v0.10.2 From 4f031fa9f188b2b0641ac20087d9e16bcfb4e49d Mon Sep 17 00:00:00 2001 From: Ronald Wahl Date: Thu, 6 Nov 2014 11:52:13 +0100 Subject: mac80211: Fix regression that triggers a kernel BUG with CCMP Commit 7ec7c4a9a686c608315739ab6a2b0527a240883c (mac80211: port CCMP to cryptoapi's CCM driver) introduced a regression when decrypting empty packets (data_len == 0). This will lead to backtraces like: (scatterwalk_start) from [] (scatterwalk_map_and_copy+0x2c/0xa8) (scatterwalk_map_and_copy) from [] (crypto_ccm_decrypt+0x7c/0x25c) (crypto_ccm_decrypt) from [] (ieee80211_aes_ccm_decrypt+0x160/0x170) (ieee80211_aes_ccm_decrypt) from [] (ieee80211_crypto_ccmp_decrypt+0x1ac/0x238) (ieee80211_crypto_ccmp_decrypt) from [] (ieee80211_rx_handlers+0x870/0x1d24) (ieee80211_rx_handlers) from [] (ieee80211_prepare_and_rx_handle+0x8a0/0x91c) (ieee80211_prepare_and_rx_handle) from [] (ieee80211_rx+0x568/0x730) (ieee80211_rx) from [] (__carl9170_rx+0x94c/0xa20) (__carl9170_rx) from [] (carl9170_rx_stream+0x1fc/0x320) (carl9170_rx_stream) from [] (carl9170_usb_tasklet+0x80/0xc8) (carl9170_usb_tasklet) from [] (tasklet_hi_action+0x88/0xcc) (tasklet_hi_action) from [] (__do_softirq+0xcc/0x200) (__do_softirq) from [] (irq_exit+0x80/0xe0) (irq_exit) from [] (handle_IRQ+0x64/0x80) (handle_IRQ) from [] (__irq_svc+0x40/0x4c) (__irq_svc) from [] (arch_cpu_idle+0x2c/0x34) Such packets can appear for example when using the carl9170 wireless driver because hardware sometimes generates garbage when the internal FIFO overruns. This patch adds an additional length check. Cc: stable@vger.kernel.org Fixes: 7ec7c4a9a686 ("mac80211: port CCMP to cryptoapi's CCM driver") Acked-by: Ard Biesheuvel Signed-off-by: Ronald Wahl Signed-off-by: Johannes Berg diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index ec24378..09d9caa 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -53,6 +53,9 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, __aligned(__alignof__(struct aead_request)); struct aead_request *aead_req = (void *) aead_req_data; + if (data_len == 0) + return -EINVAL; + memset(aead_req, 0, sizeof(aead_req_data)); sg_init_one(&pt, data, data_len); -- cgit v0.10.2 From 67e225009bb15403341d313f51326113c61af7df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Nov 2014 13:04:49 +0100 Subject: ALSA: usb-audio: Trigger PCM XRUN at XRUN The usb-audio driver detects XRUN at its complete callback, but the actual code to trigger PCM XRUN is commented out because it caused deadlock in the past. This patch revives the PCM trigger properly. It resulted in more than just enabling snd_pcm_stop(), but it had to deduce the PCM substream with proper NULL checks and holds the stream lock around the call. Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 167d0c1..a467991 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -348,6 +348,8 @@ static void snd_complete_urb(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; + struct snd_pcm_substream *substream; + unsigned long flags; int err; if (unlikely(urb->status == -ENOENT || /* unlinked */ @@ -364,8 +366,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; if (snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned long flags; - spin_lock_irqsave(&ep->lock, flags); list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); spin_unlock_irqrestore(&ep->lock, flags); @@ -389,7 +389,12 @@ static void snd_complete_urb(struct urb *urb) return; usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + if (ep->data_subs && ep->data_subs->pcm_substream) { + substream = ep->data_subs->pcm_substream; + snd_pcm_stream_lock_irqsave(substream, flags); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(substream, flags); + } exit_clear: clear_bit(ctx->index, &ep->active_mask); -- cgit v0.10.2 From 835f252c6debd204fcd607c79975089b1ecd3472 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Thu, 6 Nov 2014 17:46:21 +0800 Subject: aio: fix uncorrent dirty pages accouting when truncating AIO ring buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://bugzilla.kernel.org/show_bug.cgi?id=86831 Markus reported that when shutting down mysqld (with AIO support, on a ext3 formatted Harddrive) leads to a negative number of dirty pages (underrun to the counter). The negative number results in a drastic reduction of the write performance because the page cache is not used, because the kernel thinks it is still 2 ^ 32 dirty pages open. Add a warn trace in __dec_zone_state will catch this easily: static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) { atomic_long_dec(&zone->vm_stat[item]); + WARN_ON_ONCE(item == NR_FILE_DIRTY && atomic_long_read(&zone->vm_stat[item]) < 0); atomic_long_dec(&vm_stat[item]); } [ 21.341632] ------------[ cut here ]------------ [ 21.346294] WARNING: CPU: 0 PID: 309 at include/linux/vmstat.h:242 cancel_dirty_page+0x164/0x224() [ 21.355296] Modules linked in: wutbox_cp sata_mv [ 21.359968] CPU: 0 PID: 309 Comm: kworker/0:1 Not tainted 3.14.21-WuT #80 [ 21.366793] Workqueue: events free_ioctx [ 21.370760] [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [ 21.378562] [] (show_stack) from [] (dump_stack+0x24/0x28) [ 21.385840] [] (dump_stack) from [] (warn_slowpath_common+0x84/0x9c) [ 21.393976] [] (warn_slowpath_common) from [] (warn_slowpath_null+0x2c/0x34) [ 21.402800] [] (warn_slowpath_null) from [] (cancel_dirty_page+0x164/0x224) [ 21.411524] [] (cancel_dirty_page) from [] (truncate_inode_page+0x8c/0x158) [ 21.420272] [] (truncate_inode_page) from [] (truncate_inode_pages_range+0x11c/0x53c) [ 21.429890] [] (truncate_inode_pages_range) from [] (truncate_pagecache+0x88/0xac) [ 21.439252] [] (truncate_pagecache) from [] (truncate_setsize+0x5c/0x74) [ 21.447731] [] (truncate_setsize) from [] (put_aio_ring_file.isra.14+0x34/0x90) [ 21.456826] [] (put_aio_ring_file.isra.14) from [] (aio_free_ring+0x20/0xcc) [ 21.465660] [] (aio_free_ring) from [] (free_ioctx+0x24/0x44) [ 21.473190] [] (free_ioctx) from [] (process_one_work+0x134/0x47c) [ 21.481132] [] (process_one_work) from [] (worker_thread+0x130/0x414) [ 21.489350] [] (worker_thread) from [] (kthread+0xd4/0xec) [ 21.496621] [] (kthread) from [] (ret_from_fork+0x14/0x20) [ 21.503884] ---[ end trace 79c4bf42c038c9a1 ]--- The cause is that we set the aio ring file pages as *DIRTY* via SetPageDirty (bypasses the VFS dirty pages increment) when init, and aio fs uses *default_backing_dev_info* as the backing dev, which does not disable the dirty pages accounting capability. So truncating aio ring file will contribute to accounting dirty pages (VFS dirty pages decrement), then error occurs. The original goal is keeping these pages in memory (can not be reclaimed or swapped) in life-time via marking it dirty. But thinking more, we have already pinned pages via elevating the page's refcount, which can already achieve the goal, so the SetPageDirty seems unnecessary. In order to fix the issue, using the __set_page_dirty_no_writeback instead of the nop .set_page_dirty, and dropped the SetPageDirty (don't manually set the dirty flags, don't disable set_page_dirty(), rely on default behaviour). With the above change, the dirty pages accounting can work well. But as we known, aio fs is an anonymous one, which should never cause any real write-back, we can ignore the dirty pages (write back) accounting by disabling the dirty pages (write back) accounting capability. So we introduce an aio private backing dev info (disabled the ACCT_DIRTY/WRITEBACK/ACCT_WB capabilities) to replace the default one. Reported-by: Markus Königshaus Signed-off-by: Gu Zheng Cc: stable Acked-by: Andrew Morton Signed-off-by: Benjamin LaHaise diff --git a/fs/aio.c b/fs/aio.c index 7337500..0ff7c46 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -165,6 +165,15 @@ static struct vfsmount *aio_mnt; static const struct file_operations aio_ring_fops; static const struct address_space_operations aio_ctx_aops; +/* Backing dev info for aio fs. + * -no dirty page accounting or writeback happens + */ +static struct backing_dev_info aio_fs_backing_dev_info = { + .name = "aiofs", + .state = 0, + .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_MAP_COPY, +}; + static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages) { struct qstr this = QSTR_INIT("[aio]", 5); @@ -176,6 +185,7 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages) inode->i_mapping->a_ops = &aio_ctx_aops; inode->i_mapping->private_data = ctx; + inode->i_mapping->backing_dev_info = &aio_fs_backing_dev_info; inode->i_size = PAGE_SIZE * nr_pages; path.dentry = d_alloc_pseudo(aio_mnt->mnt_sb, &this); @@ -220,6 +230,9 @@ static int __init aio_setup(void) if (IS_ERR(aio_mnt)) panic("Failed to create aio fs mount."); + if (bdi_init(&aio_fs_backing_dev_info)) + panic("Failed to init aio fs backing dev info."); + kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC); @@ -281,11 +294,6 @@ static const struct file_operations aio_ring_fops = { .mmap = aio_ring_mmap, }; -static int aio_set_page_dirty(struct page *page) -{ - return 0; -} - #if IS_ENABLED(CONFIG_MIGRATION) static int aio_migratepage(struct address_space *mapping, struct page *new, struct page *old, enum migrate_mode mode) @@ -357,7 +365,7 @@ out: #endif static const struct address_space_operations aio_ctx_aops = { - .set_page_dirty = aio_set_page_dirty, + .set_page_dirty = __set_page_dirty_no_writeback, #if IS_ENABLED(CONFIG_MIGRATION) .migratepage = aio_migratepage, #endif @@ -412,7 +420,6 @@ static int aio_setup_ring(struct kioctx *ctx) pr_debug("pid(%d) page[%d]->count=%d\n", current->pid, i, page_count(page)); SetPageUptodate(page); - SetPageDirty(page); unlock_page(page); ctx->ring_pages[i] = page; -- cgit v0.10.2 From 2eb04ae010a8fb165ba7aa56e9aa8e7980887dee Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 27 Oct 2014 16:32:35 +0100 Subject: ARM: mvebu: add missing of_node_put() call in coherency.c There is a missing of_node_put() to decrement the device_node reference counter after a of_find_matching_node() in coherency_init(). Fixes: 501f928e0097 ("ARM: mvebu: add a coherency_available() call") Cc: # v3.16+ Signed-off-by: Thomas Petazzoni Acked-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1414423955-5933-4-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 2bdc323..044b511 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -400,6 +400,8 @@ int __init coherency_init(void) type == COHERENCY_FABRIC_TYPE_ARMADA_380) armada_375_380_coherency_init(np); + of_node_put(np); + return 0; } -- cgit v0.10.2 From 0a8727e69778683495058852f783eeda141a754e Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 6 Nov 2014 13:54:27 -0600 Subject: spi: dw: Fix dynamic speed change. An IOCTL call that calls spi_setup() and then dw_spi_setup() will overwrite the persisted last transfer speed. On each transfer, the SPI speed is compared to the last transfer speed to determine if the clock divider registers need to be updated (did the speed change?). This bug was observed with the spidev driver using spi-config to update the max transfer speed. This fix: Don't overwrite the persisted last transaction clock speed when updating the SPI parameters in dw_spi_setup(). On the next transaction, the new speed won't match the persisted last speed and the hardware registers will be updated. On initialization, the persisted last transaction clock speed will be 0 but will be updated after the first SPI transaction. Move zeroed clock divider check into clock change test because chip->clk_div is zero on startup and would cause a divide-by-zero error. The calculation was wrong as well (can't support odd #). Reported-by: Vlastimil Setka Signed-off-by: Vlastimil Setka Signed-off-by: Thor Thayer Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 7292158..332a6ab 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -376,9 +376,6 @@ static void pump_transfers(unsigned long data) chip = dws->cur_chip; spi = message->spi; - if (unlikely(!chip->clk_div)) - chip->clk_div = dws->max_freq / chip->speed_hz; - if (message->state == ERROR_STATE) { message->status = -EIO; goto early_exit; @@ -419,7 +416,7 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) { speed = chip->speed_hz; - if (transfer->speed_hz != speed) { + if ((transfer->speed_hz != speed) || (!chip->clk_div)) { speed = transfer->speed_hz; /* clk_div doesn't support odd number */ @@ -581,7 +578,6 @@ static int dw_spi_setup(struct spi_device *spi) dev_err(&spi->dev, "No max speed HZ parameter\n"); return -EINVAL; } - chip->speed_hz = spi->max_speed_hz; chip->tmode = 0; /* Tx & Rx */ /* Default SPI mode is SCPOL = 0, SCPH = 0 */ -- cgit v0.10.2 From 31584ed18c073176a7ad96ddbfd09765e21e813d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 7 Nov 2014 14:12:34 +0100 Subject: ALSA: snd_ctl_activate_id(): Fix index look-up We want to know the offset for the id that was passed to the function, not the offset of the first id of the control (which is always 0). Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/sound/core/control.c b/sound/core/control.c index 99aa3aa..bb96a46 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -572,7 +572,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, ret = -ENOENT; goto unlock; } - index_offset = snd_ctl_get_ioff(kctl, &kctl->id); + index_offset = snd_ctl_get_ioff(kctl, id); vd = &kctl->vd[index_offset]; ret = 0; if (active) { -- cgit v0.10.2 From 532425a7a7738b479406496ca4ad6b3247688e44 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 6 Nov 2014 19:56:49 +0800 Subject: ARM: dts: sun6i: Re-parent ahb1_mux to pll6 as required by dma controller The dma controller requires that the ahb1 bus clock be driven by pll6 for peripheral access to work. Previously this was done in the dma controller driver, but was since removed as part of a series to unify the ahb1_mux and ahb1 clock drivers, in 14e0e28 dmaengine: sun6i: Remove obsolete clk muxing code Unfortunately the rest of that series did not make it, leaving us with broken dma on sun6i. This patch reparents ahb1_mux to pll6 using the DT assigned-clocks properties in the dma controller node. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 543f895..2e652e2 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -361,6 +361,10 @@ clocks = <&ahb1_gates 6>; resets = <&ahb1_rst 6>; #dma-cells = <1>; + + /* DMA controller requires AHB1 clocked from PLL6 */ + assigned-clocks = <&ahb1_mux>; + assigned-clock-parents = <&pll6>; }; mmc0: mmc@01c0f000 { -- cgit v0.10.2 From 1fb8510cdb5b7befe8a59f533c7fc12ef0dac73e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Nov 2014 17:08:28 +0100 Subject: ALSA: pcm: Add snd_pcm_stop_xrun() helper Add a new helper function snd_pcm_stop_xrun() to the standard sequnce lock/snd_pcm_stop(XRUN)/unlock by a single call, and replace the existing open codes with this helper. The function checks the PCM running state to prevent setting the wrong state, too, for more safety. Signed-off-by: Takashi Iwai diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index 4056989..ac3cd74 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -173,9 +173,7 @@ static void saa7134_irq_alsa_done(struct saa7134_dev *dev, dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count, dev->dmasound.bufsize, dev->dmasound.blocks); spin_unlock(&dev->slock); - snd_pcm_stream_lock(dev->dmasound.substream); - snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(dev->dmasound.substream); + snd_pcm_stop_xrun(dev->dmasound.substream); return; } diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0b8daee..40289ec 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -506,6 +506,7 @@ int snd_pcm_status(struct snd_pcm_substream *substream, int snd_pcm_start(struct snd_pcm_substream *substream); int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); +int snd_pcm_stop_xrun(struct snd_pcm_substream *substream); #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); int snd_pcm_suspend_all(struct snd_pcm *pcm); diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index a61d7a9..01f8fdc 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -200,9 +200,7 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) } else { printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", dma_ch, dcsr); - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stop_xrun(substream); } } EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index dfb5031..a3d1221 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1098,6 +1098,29 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream) SNDRV_PCM_STATE_SETUP); } +/** + * snd_pcm_stop_xrun - stop the running streams as XRUN + * @substream: the PCM substream instance + * @state: PCM state after stopping the stream + * + * This stops the given running substream (and all linked substreams) as XRUN. + * Unlike snd_pcm_stop(), this function takes the substream lock by itself. + * + * Return: Zero if successful, or a negative error code. + */ +int snd_pcm_stop_xrun(struct snd_pcm_substream *substream) +{ + unsigned long flags; + int ret = 0; + + snd_pcm_stream_lock_irqsave(substream, flags); + if (snd_pcm_running(substream)) + ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(substream, flags); + return ret; +} +EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun); + /* * pause callbacks */ diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 95fc2eaf..3badc70 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -1006,11 +1006,7 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s) struct snd_pcm_substream *pcm; pcm = ACCESS_ONCE(s->pcm); - if (pcm) { - snd_pcm_stream_lock_irq(pcm); - if (snd_pcm_running(pcm)) - snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(pcm); - } + if (pcm) + snd_pcm_stop_xrun(pcm); } EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 7ac9443..48d6dca 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -131,14 +131,8 @@ static void isight_samples(struct isight *isight, static void isight_pcm_abort(struct isight *isight) { - unsigned long 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); - } + if (ACCESS_ONCE(isight->pcm_active)) + snd_pcm_stop_xrun(isight->pcm); } static void isight_dropped_samples(struct isight *isight, unsigned int total) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index ac66b32..ff9f9f1c 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -769,10 +769,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) s->number); ds->drained_count++; if (ds->drained_count > 20) { - unsigned long flags; - snd_pcm_stream_lock_irqsave(s, flags); - snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(s, flags); + snd_pcm_stop_xrun(s); continue; } } else { diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 7895c5d..9c1c445 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -688,9 +688,7 @@ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma) if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); - snd_pcm_stream_lock(dma->substream); - snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(dma->substream); + snd_pcm_stop_xrun(dma->substream); } /* diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 3c32413..b2f63e0 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -638,9 +638,7 @@ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip, if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); - snd_pcm_stream_lock(dma->substream); - snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(dma->substream); + snd_pcm_stop_xrun(dma->substream); } /* diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index b79a2a8..33fb3bb 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -80,9 +80,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, /* stop RX and capture: will be enabled again at restart */ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stop_xrun(substream); /* now drain RHR and read status to remove xrun condition */ ssc_readx(prtd->ssc->regs, SSC_RHR); diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index a609aaf..b2b1088 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -151,14 +151,7 @@ static const struct snd_pcm_hardware fsl_dma_hardware = { */ static void fsl_dma_abort_stream(struct snd_pcm_substream *substream) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(substream, flags); - - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - - snd_pcm_stream_unlock_irqrestore(substream, flags); + snd_pcm_stop_xrun(substream); } /** diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba40489..36f4115 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -679,25 +679,16 @@ int usb6fire_pcm_init(struct sfire_chip *chip) void usb6fire_pcm_abort(struct sfire_chip *chip) { struct pcm_runtime *rt = chip->pcm; - unsigned long flags; int i; if (rt) { rt->panic = true; - if (rt->playback.instance) { - snd_pcm_stream_lock_irqsave(rt->playback.instance, flags); - snd_pcm_stop(rt->playback.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->playback.instance, flags); - } + if (rt->playback.instance) + snd_pcm_stop_xrun(rt->playback.instance); - if (rt->capture.instance) { - snd_pcm_stream_lock_irqsave(rt->capture.instance, flags); - snd_pcm_stop(rt->capture.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->capture.instance, flags); - } + if (rt->capture.instance) + snd_pcm_stop_xrun(rt->capture.instance); for (i = 0; i < PCM_N_URBS; i++) { usb_poison_urb(&rt->in_urbs[i].instance); diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a467991..03b0744 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -391,9 +391,7 @@ static void snd_complete_urb(struct urb *urb) usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); if (ep->data_subs && ep->data_subs->pcm_substream) { substream = ep->data_subs->pcm_substream; - snd_pcm_stream_lock_irqsave(substream, flags); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(substream, flags); + snd_pcm_stop_xrun(substream); } exit_clear: diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index a1bab14..9581089 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -613,24 +613,14 @@ static int start_usb_playback(struct ua101 *ua) static void abort_alsa_capture(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->capture.substream, flags); - snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->capture.substream, flags); - } + if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->capture.substream); } static void abort_alsa_playback(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->playback.substream, flags); - snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->playback.substream, flags); - } + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->playback.substream); } static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index a63330d..61d5dc2 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -272,13 +272,8 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y) for (s = 0; s < 4; s++) { struct snd_usX2Y_substream *subs = usX2Y->subs[s]; if (subs) { - if (atomic_read(&subs->state) >= state_PRERUNNING) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(subs->pcm_substream, flags); - snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(subs->pcm_substream, flags); - } + if (atomic_read(&subs->state) >= state_PRERUNNING) + snd_pcm_stop_xrun(subs->pcm_substream); for (u = 0; u < NRURBS; u++) { struct urb *urb = subs->urb[u]; if (NULL != urb) -- cgit v0.10.2 From d4b8fc66f770e9b79830cfe6c342846293b99fda Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 9 Nov 2014 18:21:23 +0100 Subject: ALSA: usb-audio: Allow multiple entries for the same iface in composite quirk Currently the composite quirk doesn't work when multiple entries are assigned to the same interface because it marks the interface as claimed then checks whether the interface has been already claimed for the secondary entry. But, if you look at the code, you'll notice that multiple entries are allowed if the entry is the current interface; i.e. the current behavior is anyway inconsistent, and this is an unintended shortcoming. This patch fixes the problem by marking the relevant interfaces as claimed after applying the all composite entries. This fix will be needed for the upcoming enhancements for Digidesign Mbox 1 quirks. Reviewed-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d2aa45a..e9ff3a6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -58,9 +58,17 @@ static int create_composite_quirk(struct snd_usb_audio *chip, err = snd_usb_create_quirk(chip, iface, driver, quirk); if (err < 0) return err; - if (quirk->ifnum != probed_ifnum) + } + + for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); + if (!iface) + continue; + if (quirk->ifnum != probed_ifnum && + !usb_interface_claimed(iface)) usb_driver_claim_interface(driver, iface, (void *)-1L); } + return 0; } -- cgit v0.10.2 From a926a12b5f11007d0ba9eb2e083d86054fb29a06 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Thu, 30 Oct 2014 14:57:56 +0900 Subject: ARM: shmobile: r8a7790: Fix SD3CKCR address Signed-off-by: Shinobu Uehara Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c index 126ddaf..f622652 100644 --- a/arch/arm/mach-shmobile/clock-r8a7790.c +++ b/arch/arm/mach-shmobile/clock-r8a7790.c @@ -68,7 +68,7 @@ #define SDCKCR 0xE6150074 #define SD2CKCR 0xE6150078 -#define SD3CKCR 0xE615007C +#define SD3CKCR 0xE615026C #define MMC0CKCR 0xE6150240 #define MMC1CKCR 0xE6150244 #define SSPCKCR 0xE6150248 -- cgit v0.10.2 From 85eb968e84686faa507daa3c5cfdfbfefc7eda95 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 5 Nov 2014 21:59:34 +0100 Subject: ARM: shmobile: r8a7740 legacy: Add missing INTCA clock for irqpin module This clock drives the irqpin controller modules. Before, it was assumed enabled by the bootloader or reset state. By making it available to the driver, we make sure it gets enabled when needed, and allow it to be managed by system or runtime PM. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c index 0794f04..16a6b7c 100644 --- a/arch/arm/mach-shmobile/clock-r8a7740.c +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -455,7 +455,7 @@ enum { MSTP128, MSTP127, MSTP125, MSTP116, MSTP111, MSTP100, MSTP117, - MSTP230, + MSTP230, MSTP229, MSTP222, MSTP218, MSTP217, MSTP216, MSTP214, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, @@ -479,6 +479,7 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */ + [MSTP229] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 29, 0), /* INTCA */ [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */ [MSTP218] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* DMAC1 */ [MSTP217] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 17, 0), /* DMAC2 */ @@ -575,6 +576,10 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP218]), CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP222]), CLKDEV_DEV_ID("e6cd0000.serial", &mstp_clks[MSTP222]), + CLKDEV_DEV_ID("renesas_intc_irqpin.0", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.1", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.2", &mstp_clks[MSTP229]), + CLKDEV_DEV_ID("renesas_intc_irqpin.3", &mstp_clks[MSTP229]), CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP230]), CLKDEV_DEV_ID("e6cc0000.serial", &mstp_clks[MSTP230]), -- cgit v0.10.2 From 4f37828d4d69a46830e0525a065da9847fc7a819 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 5 Nov 2014 11:04:33 +0100 Subject: ARM: shmobile: r8a7740 legacy: Correct IIC0 parent clock According to the datasheet, the operating clock for IIC0 is the HPP (RT Peri) clock, not the SUB (Peri) clock. Both clocks run at the same speed (50 Mhz). This is consistent with IIC0 being located in the A4R PM domain, and IIC1 in the A3SP PM domain. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c index 16a6b7c..19df9cb 100644 --- a/arch/arm/mach-shmobile/clock-r8a7740.c +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -474,7 +474,7 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_S], SMSTPCR1, 27, 0), /* CEU20 */ [MSTP125] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */ [MSTP117] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 17, 0), /* LCDC1 */ - [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */ + [MSTP116] = SH_CLK_MSTP32(&div4_clks[DIV4_HPP], SMSTPCR1, 16, 0), /* IIC0 */ [MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */ [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ -- cgit v0.10.2 From edd7b938637701567a54306adb27cfb4345fedc5 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Thu, 30 Oct 2014 14:57:57 +0900 Subject: ARM: shmobile: r8a7790: Fix SD3CKCR address to device tree Signed-off-by: Shinobu Uehara Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index d0e1773..e20affe 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -666,9 +666,9 @@ #clock-cells = <0>; clock-output-names = "sd2"; }; - sd3_clk: sd3_clk@e615007c { + sd3_clk: sd3_clk@e615026c { compatible = "renesas,r8a7790-div6-clock", "renesas,cpg-div6-clock"; - reg = <0 0xe615007c 0 4>; + reg = <0 0xe615026c 0 4>; clocks = <&pll1_div2_clk>; #clock-cells = <0>; clock-output-names = "sd3"; -- cgit v0.10.2 From b89ff7c3c2dee189489a5f45eb8d72e106179299 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 5 Nov 2014 11:04:34 +0100 Subject: ARM: shmobile: r8a7740 dtsi: Correct IIC0 parent clock According to the datasheet, the operating clock for IIC0 is the HPP (RT Peri) clock, not the SUB (Peri) clock. Both clocks run at the same speed (50 Mhz). This is consistent with IIC0 being located in the A4R PM domain, and IIC1 in the A3SP PM domain. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman diff --git a/arch/arm/boot/dts/r8a7740.dtsi b/arch/arm/boot/dts/r8a7740.dtsi index d46c213..eed697a 100644 --- a/arch/arm/boot/dts/r8a7740.dtsi +++ b/arch/arm/boot/dts/r8a7740.dtsi @@ -433,7 +433,7 @@ clocks = <&cpg_clocks R8A7740_CLK_S>, <&cpg_clocks R8A7740_CLK_S>, <&sub_clk>, <&cpg_clocks R8A7740_CLK_B>, - <&sub_clk>, <&sub_clk>, + <&cpg_clocks R8A7740_CLK_HPP>, <&sub_clk>, <&cpg_clocks R8A7740_CLK_B>; #clock-cells = <1>; renesas,clock-indices = < -- cgit v0.10.2 From 48b217aa43abc8c3545bb9b4d7a5b525b71d6ac2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 07:38:22 +0100 Subject: ALSA: pcm: Fix document for snd_pcm_stop_xrun() Fix a copy & paste error: Warning(sound/core/pcm_native.c:1112): Excess function parameter 'state' description in 'snd_pcm_stop_xrun' The state argument was dropped from snd_pcm_stop_xrun(). Reported-by: kbuild test robot Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a3d1221..095d957 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1101,7 +1101,6 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream) /** * snd_pcm_stop_xrun - stop the running streams as XRUN * @substream: the PCM substream instance - * @state: PCM state after stopping the stream * * This stops the given running substream (and all linked substreams) as XRUN. * Unlike snd_pcm_stop(), this function takes the substream lock by itself. -- cgit v0.10.2 From 85a8181329a919d58b7ef99211251f47d5e1049e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 07:41:59 +0100 Subject: ALSA: usb-audio: Fix Oops by composite quirk enhancement The quirk argument itself was used as iterator, so it cannot be taken back to the original value, obviously. Fixes: d4b8fc66f770 ('ALSA: usb-audio: Allow multiple entries for the same iface in composite quirk') Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e9ff3a6..809d7fa 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -43,12 +43,13 @@ static int create_composite_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk_comp) { int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + const struct snd_usb_audio_quirk *quirk; int err; - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); if (!iface) continue; @@ -60,7 +61,7 @@ static int create_composite_quirk(struct snd_usb_audio *chip, return err; } - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); if (!iface) continue; -- cgit v0.10.2 From ffcfe30ebd8dd703d0fc4324ffe56ea21f5479f4 Mon Sep 17 00:00:00 2001 From: Preston Fick Date: Fri, 7 Nov 2014 23:26:11 -0600 Subject: USB: serial: cp210x: add IDs for CEL MeshConnect USB Stick Signed-off-by: Preston Fick Cc: stable Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index cfd009d..6c4eb3c 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -120,6 +120,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ + { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ -- cgit v0.10.2 From c123588b3b193d06588dfb51f475407f835ebfb2 Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Fri, 7 Nov 2014 17:53:40 +0300 Subject: sched/numa: Fix out of bounds read in sched_init_numa() On latest mm + KASan patchset I've got this: ================================================================== BUG: AddressSanitizer: out of bounds access in sched_init_smp+0x3ba/0x62c at addr ffff88006d4bee6c ============================================================================= BUG kmalloc-8 (Not tainted): kasan error ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in alloc_vfsmnt+0xb0/0x2c0 age=75 cpu=0 pid=0 __slab_alloc+0x4b4/0x4f0 __kmalloc_track_caller+0x15f/0x1e0 kstrdup+0x44/0x90 alloc_vfsmnt+0xb0/0x2c0 vfs_kern_mount+0x35/0x190 kern_mount_data+0x25/0x50 pid_ns_prepare_proc+0x19/0x50 alloc_pid+0x5e2/0x630 copy_process.part.41+0xdf5/0x2aa0 do_fork+0xf5/0x460 kernel_thread+0x21/0x30 rest_init+0x1e/0x90 start_kernel+0x522/0x531 x86_64_start_reservations+0x2a/0x2c x86_64_start_kernel+0x15b/0x16a INFO: Slab 0xffffea0001b52f80 objects=24 used=22 fp=0xffff88006d4befc0 flags=0x100000000004080 INFO: Object 0xffff88006d4bed20 @offset=3360 fp=0xffff88006d4bee70 Bytes b4 ffff88006d4bed10: 00 00 00 00 00 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a ........ZZZZZZZZ Object ffff88006d4bed20: 70 72 6f 63 00 6b 6b a5 proc.kk. Redzone ffff88006d4bed28: cc cc cc cc cc cc cc cc ........ Padding ffff88006d4bee68: 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZ CPU: 0 PID: 1 Comm: swapper/0 Tainted: G B 3.18.0-rc3-mm1+ #108 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 ffff88006d4be000 0000000000000000 ffff88006d4bed20 ffff88006c86fd18 ffffffff81cd0a59 0000000000000058 ffff88006d404240 ffff88006c86fd48 ffffffff811fa3a8 ffff88006d404240 ffffea0001b52f80 ffff88006d4bed20 Call Trace: dump_stack (lib/dump_stack.c:52) print_trailer (mm/slub.c:645) object_err (mm/slub.c:652) ? sched_init_smp (kernel/sched/core.c:6552 kernel/sched/core.c:7063) kasan_report_error (mm/kasan/report.c:102 mm/kasan/report.c:178) ? kasan_poison_shadow (mm/kasan/kasan.c:48) ? kasan_unpoison_shadow (mm/kasan/kasan.c:54) ? kasan_poison_shadow (mm/kasan/kasan.c:48) ? kasan_kmalloc (mm/kasan/kasan.c:311) __asan_load4 (mm/kasan/kasan.c:371) ? sched_init_smp (kernel/sched/core.c:6552 kernel/sched/core.c:7063) sched_init_smp (kernel/sched/core.c:6552 kernel/sched/core.c:7063) kernel_init_freeable (init/main.c:869 init/main.c:997) ? finish_task_switch (kernel/sched/sched.h:1036 kernel/sched/core.c:2248) ? rest_init (init/main.c:924) kernel_init (init/main.c:929) ? rest_init (init/main.c:924) ret_from_fork (arch/x86/kernel/entry_64.S:348) ? rest_init (init/main.c:924) Read of size 4 by task swapper/0: Memory state around the buggy address: ffff88006d4beb80: fc fc fc fc fc fc fc fc fc fc 00 fc fc fc fc fc ffff88006d4bec00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006d4bec80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006d4bed00: fc fc fc fc 00 fc fc fc fc fc fc fc fc fc fc fc ffff88006d4bed80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff88006d4bee00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc 04 fc ^ ffff88006d4bee80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006d4bef00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006d4bef80: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb ffff88006d4bf000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88006d4bf080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Zero 'level' (e.g. on non-NUMA system) causing out of bounds access in this line: sched_max_numa_distance = sched_domains_numa_distance[level - 1]; Fix this by exiting from sched_init_numa() earlier. Signed-off-by: Andrey Ryabinin Reviewed-by: Rik van Riel Fixes: 9942f79ba ("sched/numa: Export info needed for NUMA balancing on complex topologies") Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1415372020-1871-1-git-send-email-a.ryabinin@samsung.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6841fb4..5f12ca6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6368,6 +6368,10 @@ static void sched_init_numa(void) if (!sched_debug()) break; } + + if (!level) + return; + /* * 'level' contains the number of unique distances, excluding the * identity distance node_distance(i,i). -- cgit v0.10.2 From 6b96686ecffcbea85dcb502e4584e4a20a2bfb29 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 7 Nov 2014 15:34:54 +0100 Subject: netfilter: nft_masq: fix uninitialized range in nft_masq_{ipv4, ipv6}_eval When transferring from the original range in nf_nat_masquerade_{ipv4,ipv6}() we copy over values from stack in from min_proto/max_proto due to uninitialized range variable in both, nft_masq_{ipv4,ipv6}_eval. As we only initialize flags at this time from nft_masq struct, just zero out the rest. Fixes: 9ba1f726bec09 ("netfilter: nf_tables: add new nft_masq expression") Signed-off-by: Daniel Borkmann Acked-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index c1023c4..665de06 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr, struct nf_nat_range range; unsigned int verdict; + memset(&range, 0, sizeof(range)); range.flags = priv->flags; verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum, diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index 8a7ac68..529c119 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr, struct nf_nat_range range; unsigned int verdict; + memset(&range, 0, sizeof(range)); range.flags = priv->flags; verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out); -- cgit v0.10.2 From fb6eaf2ccca7d3580931bcb4b735101b461f38cf Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 5 Nov 2014 19:10:52 -0600 Subject: rtlwifi: Fix setting of tx descriptor for new trx flow Device RTL8192EE uses a new form of trx flow. This fix sets up the descriptors correctly. Signed-off-by: Larry Finger Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 25daa87..116f746 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1127,9 +1127,14 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) __skb_queue_tail(&ring->queue, pskb); - rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN, - &temp_one); - + if (rtlpriv->use_new_trx_flow) { + temp_one = 4; + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true, + HW_DESC_OWN, (u8 *)&temp_one); + } else { + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN, + &temp_one); + } return; } -- cgit v0.10.2 From caea2172c23465a77556b2e1d06412b532b90235 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 5 Nov 2014 19:10:53 -0600 Subject: rtlwifi: Fix errors in descriptor manipulation There are typos in the handling of the descriptor pointers where the wrong descriptor is referenced. There is also an error in which the pointer is incremented twice. Signed-off-by: Larry Finger Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 116f746..6d2b628 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1375,9 +1375,9 @@ static void _rtl_pci_free_tx_ring(struct ieee80211_hw *hw, ring->desc = NULL; if (rtlpriv->use_new_trx_flow) { pci_free_consistent(rtlpci->pdev, - sizeof(*ring->desc) * ring->entries, + sizeof(*ring->buffer_desc) * ring->entries, ring->buffer_desc, ring->buffer_desc_dma); - ring->desc = NULL; + ring->buffer_desc = NULL; } } @@ -1548,7 +1548,6 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); - ring->idx = (ring->idx + 1) % ring->entries; kfree_skb(skb); ring->idx = (ring->idx + 1) % ring->entries; } -- cgit v0.10.2 From d1cd5ba4ca8b41793f4e581dd1dbf46b7f2cf691 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 7 Nov 2014 10:05:12 -0600 Subject: rtlwifi: rtl8192se: Fix connection problems Changes in the vendor driver were added to rtlwifi, but some updates to rtl8192se were missed, and the driver could neither scan nor connect. There are other changes that will enhance performance, but this minimal set fix the basic functionality. Signed-off-by: Larry Finger Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 6d2b628..61f5d36 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -842,7 +842,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) break; } /* handle command packet here */ - if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) { + if (rtlpriv->cfg->ops->rx_command_packet && + rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) { dev_kfree_skb_any(skb); goto end; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index 00e0670..5761d5b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -1201,6 +1201,9 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw, } + if (type != NL80211_IFTYPE_AP && + rtlpriv->mac80211.link_state < MAC80211_LINKED) + bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK; rtl_write_byte(rtlpriv, (MSR), bt_msr); temp = rtl_read_dword(rtlpriv, TCR); @@ -1262,6 +1265,7 @@ void rtl92se_enable_interrupt(struct ieee80211_hw *hw) rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]); /* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */ rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F); + rtlpci->irq_enabled = true; } void rtl92se_disable_interrupt(struct ieee80211_hw *hw) @@ -1276,8 +1280,7 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw) rtlpci = rtl_pcidev(rtl_pcipriv(hw)); rtl_write_dword(rtlpriv, INTA_MASK, 0); rtl_write_dword(rtlpriv, INTA_MASK + 4, 0); - - synchronize_irq(rtlpci->pdev->irq); + rtlpci->irq_enabled = false; } static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data) diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c index 77c5b5f..4b4612f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c @@ -399,6 +399,8 @@ static bool _rtl92s_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, case 2: currentcmd = &postcommoncmd[*step]; break; + default: + return true; } if (currentcmd->cmdid == CMDID_END) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index aadba29..fb00386 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -236,6 +236,19 @@ static void rtl92s_deinit_sw_vars(struct ieee80211_hw *hw) } } +static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, + u16 index) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; + u8 *entry = (u8 *)(&ring->desc[ring->idx]); + u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN); + + if (own) + return false; + return true; +} + static struct rtl_hal_ops rtl8192se_hal_ops = { .init_sw_vars = rtl92s_init_sw_vars, .deinit_sw_vars = rtl92s_deinit_sw_vars, @@ -269,6 +282,7 @@ static struct rtl_hal_ops rtl8192se_hal_ops = { .led_control = rtl92se_led_control, .set_desc = rtl92se_set_desc, .get_desc = rtl92se_get_desc, + .is_tx_desc_closed = rtl92se_is_tx_desc_closed, .tx_polling = rtl92se_tx_polling, .enable_hw_sec = rtl92se_enable_hw_security_config, .set_key = rtl92se_set_key, @@ -306,6 +320,8 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = { .maps[MAC_RCR_ACRC32] = RCR_ACRC32, .maps[MAC_RCR_ACF] = RCR_ACF, .maps[MAC_RCR_AAP] = RCR_AAP, + .maps[MAC_HIMR] = INTA_MASK, + .maps[MAC_HIMRE] = INTA_MASK + 4, .maps[EFUSE_TEST] = REG_EFUSE_TEST, .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, -- cgit v0.10.2 From 9c3a6670863033df421c8e46f79f383cf3a922ee Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 8 Nov 2014 13:59:42 +0100 Subject: b43: fix NULL pointer dereference in b43_phy_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit phy_read and phy_write are not set for every phy any more sine this: commit d342b95dd735014a590f9051b1ba227eb54ca8f6 Author: RafaÅ‚ MiÅ‚ecki Date: Thu Jul 31 21:59:43 2014 +0200 b43: don't duplicate common PHY read/write ops b43_phy_copy() accesses phy_read and phy_write directly and will fail with some phys. This patch fixes the regression by using the b43_phy_read() and b43_phy_write() functions which should be used for read and write access. This should fix this bug report: https://bugzilla.kernel.org/show_bug.cgi?id=87731 Reported-by: Volker Kempter Tested-by: Volker Kempter Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 1dfc682..ee27b06 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -300,9 +300,7 @@ void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) { - assert_mac_suspended(dev); - dev->phy.ops->phy_write(dev, destreg, - dev->phy.ops->phy_read(dev, srcreg)); + b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg)); } void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) -- cgit v0.10.2 From 520fe7633692181bb6d1560d655fbdfbb7c05aaa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Nov 2014 14:39:44 -0600 Subject: usb: dwc3: ep0: fix for dead code commit 6856d30 (usb: dwc3: ep0: return early on NULL requests) tried to fix a minor corner case where we could dereference a NULL pointer but it also ended up introducing some dead code. Unfortunately, that dead code, if reached, could end up starving the endpoint request list because a request would never be given back when it should. Fix this by moving the check for empty request list before its first use. Reported-by: Dave Jones Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 711b230..df38e7e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -791,6 +791,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, trb = dwc->ep0_trb; + r = next_request(&ep0->request_list); + if (!r) + return; + status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); @@ -801,10 +805,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, return; } - r = next_request(&ep0->request_list); - if (!r) - return; - ur = &r->request; length = trb->size & DWC3_TRB_SIZE_MASK; -- cgit v0.10.2 From 9b520d84957d63348e87c0f2cbd21d86e1e8f2f2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 4 Nov 2014 15:54:11 +0200 Subject: iwlwifi: mvm: abort scan upon RFKILL This code existed but not for all the different FW APIs we support. Fix this. Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index b280d5d..7554f70 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -602,16 +602,6 @@ static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm) SCAN_COMPLETE_NOTIFICATION }; int ret; - if (mvm->scan_status == IWL_MVM_SCAN_NONE) - return 0; - - if (iwl_mvm_is_radio_killed(mvm)) { - ieee80211_scan_completed(mvm->hw, true); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - mvm->scan_status = IWL_MVM_SCAN_NONE; - return 0; - } - iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort, scan_abort_notif, ARRAY_SIZE(scan_abort_notif), @@ -1400,6 +1390,16 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) { + if (mvm->scan_status == IWL_MVM_SCAN_NONE) + return 0; + + if (iwl_mvm_is_radio_killed(mvm)) { + ieee80211_scan_completed(mvm->hw, true); + iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); + mvm->scan_status = IWL_MVM_SCAN_NONE; + return 0; + } + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) return iwl_mvm_scan_offload_stop(mvm, true); return iwl_mvm_cancel_regular_scan(mvm); -- cgit v0.10.2 From 87dd634ae72bb8f6d0dd12f1cbbc67c7da6dba3b Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 10 Nov 2014 19:25:22 +0200 Subject: iwlwifi: pcie: fix prph dump length The length counting previously done had an error in it, causing the length down the data dumping function to be shorter than it should be, causing the end of the data to get truncated off and lost. Cc: [3.17+] Fixes: 67c65f2cf710 ("iwlwifi: dump periphery registers to fw-error-dump") Signed-off-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 160c3eb..dd2f3f8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1894,8 +1894,7 @@ static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans, int reg; __le32 *val; - prph_len += sizeof(*data) + sizeof(*prph) + - num_bytes_in_chunk; + prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); (*data)->len = cpu_to_le32(sizeof(*prph) + -- cgit v0.10.2 From 2a9e8df00951092e825144a9968285398f8aa162 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 16:46:35 +0100 Subject: ALSA: vx: Fix missing kerneldoc parameter descriptions The file isn't processed, but it's not bad to fix beforehand. Signed-off-by: Takashi Iwai diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index e8cc169..fc05a37 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -416,6 +416,7 @@ int vx_send_rih(struct vx_core *chip, int cmd) /** * snd_vx_boot_xilinx - boot up the xilinx interface + * @chip: VX core instance * @boot: the boot record to load */ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot) @@ -538,6 +539,8 @@ EXPORT_SYMBOL(snd_vx_threaded_irq_handler); /** * snd_vx_irq_handler - interrupt handler + * @irq: irq number + * @dev: VX core instance */ irqreturn_t snd_vx_irq_handler(int irq, void *dev) { @@ -649,6 +652,8 @@ static void vx_proc_init(struct vx_core *chip) /** * snd_vx_dsp_boot - load the DSP boot + * @chip: VX core instance + * @boot: firmware data */ int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot) { @@ -669,6 +674,8 @@ EXPORT_SYMBOL(snd_vx_dsp_boot); /** * snd_vx_dsp_load - load the DSP image + * @chip: VX core instance + * @dsp: firmware data */ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp) { @@ -768,7 +775,10 @@ EXPORT_SYMBOL(snd_vx_resume); /** * snd_vx_create - constructor for struct vx_core + * @card: card instance * @hw: hardware specific record + * @ops: VX ops pointer + * @extra_size: extra byte size to allocate appending to chip * * this function allocates the instance and prepare for the hardware * initialization. diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 2d15702..52c1a8d 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -92,6 +92,7 @@ static inline unsigned long vx2_reg_addr(struct vx_core *_chip, int reg) /** * snd_vx_inb - read a byte from the register + * @chip: VX core instance * @offset: register enum */ static unsigned char vx2_inb(struct vx_core *chip, int offset) @@ -101,6 +102,7 @@ static unsigned char vx2_inb(struct vx_core *chip, int offset) /** * snd_vx_outb - write a byte on the register + * @chip: VX core instance * @offset: the register offset * @val: the value to write */ @@ -114,6 +116,7 @@ static void vx2_outb(struct vx_core *chip, int offset, unsigned char val) /** * snd_vx_inl - read a 32bit word from the register + * @chip: VX core instance * @offset: register enum */ static unsigned int vx2_inl(struct vx_core *chip, int offset) @@ -123,6 +126,7 @@ static unsigned int vx2_inl(struct vx_core *chip, int offset) /** * snd_vx_outl - write a 32bit word on the register + * @chip: VX core instance * @offset: the register enum * @val: the value to write */ @@ -223,6 +227,7 @@ static int vx2_test_xilinx(struct vx_core *_chip) /** * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. + * @chip: VX core instance * @do_write: 0 = read, 1 = set up for DMA write */ static void vx2_setup_pseudo_dma(struct vx_core *chip, int do_write) diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 92ec114..b16f42d 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -174,6 +174,7 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl, /** * snd_vxpocket_assign_resources - initialize the hardware and card instance. + * @chip: VX core instance * @port: i/o port for the card * @irq: irq number for the card * -- cgit v0.10.2 From e60b2c7fcdef03256cde864d678df240877a5e80 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 16:47:26 +0100 Subject: ALSA: hda - Fix kerneldoc errors in patch_ca0132.c Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4f7ffa8..e0383ee 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2417,7 +2417,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, * @reloc: Relocation address for loading single-segment overlays, or 0 for * no relocation * @sample_rate: sampling rate of the stream used for DSP download - * @number_channels: channels of the stream used for DSP download + * @channels: channels of the stream used for DSP download * @ovly: TRUE if overlay format is required * * Returns zero or a negative error code. @@ -2556,10 +2556,7 @@ static void dspload_post_setup(struct hda_codec *codec) } /** - * Download DSP from a DSP Image Fast Load structure. This structure is a - * linear, non-constant sized element array of structures, each of which - * contain the count of the data to be loaded, the data itself, and the - * corresponding starting chip address of the starting data location. + * dspload_image - Download DSP from a DSP Image Fast Load structure. * * @codec: the HDA codec * @fls: pointer to a fast load image @@ -2570,6 +2567,10 @@ static void dspload_post_setup(struct hda_codec *codec) * @router_chans: number of audio router channels to be allocated (0 means use * internal defaults; max is 32) * + * Download DSP from a DSP Image Fast Load structure. This structure is a + * linear, non-constant sized element array of structures, each of which + * contain the count of the data to be loaded, the data itself, and the + * corresponding starting chip address of the starting data location. * Returns zero or a negative error code. */ static int dspload_image(struct hda_codec *codec, -- cgit v0.10.2 From 3f60c87d129acbb232afbc7269c726d009a01869 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 17:09:22 +0100 Subject: ALSA: mixart: Fix kerneldoc comments Signed-off-by: Takashi Iwai diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 581e1e7..9996a4d 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -37,10 +37,11 @@ /** * wait for a value on a peudo register, exit with a timeout * - * @param mgr pointer to miXart manager structure - * @param offset unsigned pseudo_register base + offset of value - * @param value value - * @param timeout timeout in centisenconds + * @mgr: pointer to miXart manager structure + * @offset: unsigned pseudo_register base + offset of value + * @is_egal: wait for the equal value + * @value: value + * @timeout: timeout in centisenconds */ static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr, u32 offset, int is_egal, -- cgit v0.10.2 From ddcecf6b6ae7b91c8735e52f50cd403ee9cbe298 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Nov 2014 17:24:26 +0100 Subject: ALSA: Fix invalid kerneldoc markers They are no real kerneldoc comments, so drop such markers. Signed-off-by: Takashi Iwai diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index d956c35..b357f1a 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -74,14 +74,14 @@ struct hdspm_config { #define SNDRV_HDSPM_IOCTL_GET_CONFIG \ _IOR('H', 0x41, struct hdspm_config) -/** +/* * If there's a TCO (TimeCode Option) board installed, * there are further options and status data available. * The hdspm_ltc structure contains the current SMPTE * timecode and some status information and can be * obtained via SNDRV_HDSPM_IOCTL_GET_LTC or in the * hdspm_status struct. - **/ + */ enum hdspm_ltc_format { format_invalid, @@ -113,11 +113,11 @@ struct hdspm_ltc { #define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) -/** +/* * The status data reflects the device's current state * as determined by the card's configuration and * connection status. - **/ + */ enum hdspm_sync { hdspm_sync_no_lock = 0, @@ -171,9 +171,9 @@ struct hdspm_status { #define SNDRV_HDSPM_IOCTL_GET_STATUS \ _IOR('H', 0x47, struct hdspm_status) -/** +/* * Get information about the card and its add-ons. - **/ + */ #define HDSPM_ADDON_TCO 1 diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index e223de1..15933f9 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -180,7 +180,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard."); /* From 0x50 - 0x5f, last samples captured */ -/** +/* * The hardware has 3 channels for playback and 1 for capture. * - channel 0 is the front channel * - channel 1 is the rear channel diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h index 49d36bd..469bcc6 100644 --- a/sound/pci/lx6464es/lx_defs.h +++ b/sound/pci/lx6464es/lx_defs.h @@ -175,7 +175,7 @@ enum buffer_flags { BF_ZERO = 0x00, /* no flags (init).*/ }; -/** +/* * Stream Flags definitions */ enum stream_flags { diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index e09348c1..3342705 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2201,10 +2201,10 @@ static inline int hdspm_get_pll_freq(struct hdspm *hdspm) return rate; } -/** +/* * Calculate the real sample rate from the * current DDS value. - **/ + */ static int hdspm_get_system_sample_rate(struct hdspm *hdspm) { unsigned int rate; @@ -2270,9 +2270,9 @@ static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the WordClock sample rate class for the given card. - **/ + */ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) { int status; @@ -2295,9 +2295,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) } -/** +/* * Returns the TCO sample rate class for the given card. - **/ + */ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) { int status; @@ -2321,9 +2321,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) } -/** +/* * Returns the SYNC_IN sample rate class for the given card. - **/ + */ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) { int status; @@ -2343,9 +2343,9 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) return 0; } -/** +/* * Returns the AES sample rate class for the given card. - **/ + */ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) { int timecode; @@ -2361,10 +2361,10 @@ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) return 0; } -/** +/* * Returns the sample rate class for input source for * 'new style' cards like the AIO and RayDAT. - **/ + */ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) { int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); @@ -2512,10 +2512,10 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the system clock mode for the given card. * @returns 0 - master, 1 - slave - **/ + */ static int hdspm_system_clock_mode(struct hdspm *hdspm) { switch (hdspm->io_type) { @@ -2534,10 +2534,10 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm) } -/** +/* * Sets the system clock mode. * @param mode 0 - master, 1 - slave - **/ + */ static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) { hdspm_set_toggle_setting(hdspm, @@ -2692,11 +2692,11 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the current preferred sync reference setting. * The semantics of the return value are depending on the * card, please see the comments for clarification. - **/ + */ static int hdspm_pref_sync_ref(struct hdspm * hdspm) { switch (hdspm->io_type) { @@ -2795,11 +2795,11 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm) } -/** +/* * Set the preferred sync reference to . The semantics * of are depending on the card type, see the comments * for clarification. - **/ + */ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { int p = 0; @@ -4101,9 +4101,9 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, -/** +/* * TCO controls - **/ + */ static void hdspm_tco_write(struct hdspm *hdspm) { unsigned int tc[4] = { 0, 0, 0, 0}; @@ -5403,7 +5403,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) HDSPM_midi2IRQPending | HDSPM_midi3IRQPending); /* now = get_cycles(); */ - /** + /* * LAT_2..LAT_0 period counter (win) counter (mac) * 6 4096 ~256053425 ~514672358 * 5 2048 ~128024983 ~257373821 @@ -5412,7 +5412,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) * 2 256 ~16003039 ~32260176 * 1 128 ~7998738 ~16194507 * 0 64 ~3998231 ~8191558 - **/ + */ /* dev_info(hdspm->card->dev, "snd_hdspm_interrupt %llu @ %llx\n", now-hdspm->last_interrupt, status & 0xFFC0); -- cgit v0.10.2 From 2196937e12b1b4ba139806d132647e1651d655df Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 10 Nov 2014 17:11:21 +0100 Subject: netfilter: ipset: small potential read beyond the end of buffer We could be reading 8 bytes into a 4 byte buffer here. It seems harmless but adding a check is the right thing to do and it silences a static checker warning. Signed-off-by: Dan Carpenter Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 86f9d76..d259da3 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) if (*op < IP_SET_OP_VERSION) { /* Check the version at the beginning of operations */ struct ip_set_req_version *req_version = data; + + if (*len < sizeof(struct ip_set_req_version)) { + ret = -EINVAL; + goto done; + } + if (req_version->version != IPSET_PROTOCOL) { ret = -EPROTO; goto done; -- cgit v0.10.2 From d497a82fb18ed4b73c08f8b5a0935f937e2ea1fb Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Wed, 12 Nov 2014 01:09:54 +1100 Subject: ALSA: usb-audio: Add mixer control for Digidesign Mbox 1 clock source This patch provides the infrastructure for the Digidesign Mbox 1 to have a mixer control for selecting the clock source. Valid options are Internal and S/PDIF external sync. A non-documented command is sent to the device to enable this feature found by reverse engineering and bus snooping. Signed-off-by: Damien Zammit Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index d1d72ff..1994d41 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -179,6 +179,11 @@ static struct usbmix_name_map audigy2nx_map[] = { { 0 } /* terminator */ }; +static struct usbmix_name_map mbox1_map[] = { + { 1, "Clock" }, + { 0 } /* terminator */ +}; + static struct usbmix_selector_map c400_selectors[] = { { .id = 0x80, @@ -416,6 +421,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = aureon_51_2_map, }, { + .id = USB_ID(0x0dba, 0x1000), + .map = mbox1_map, + }, + { .id = USB_ID(0x13e5, 0x0001), .map = scratch_live_map, .ignore_ctl_error = 1, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 3980bf5..4520316 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -565,6 +565,131 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* Digidesign Mbox 1 clock source switch (internal/spdif) */ + +static int snd_mbox1_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value; + return 0; +} + +static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip; + struct usb_mixer_interface *mixer; + int err; + bool cur_val, new_val; + unsigned char buff[3]; + + cur_val = kctl->private_value; + new_val = ucontrol->value.enumerated.item[0]; + + mixer = snd_kcontrol_chip(kctl); + if (snd_BUG_ON(!mixer)) + return -EINVAL; + + chip = mixer->chip; + if (snd_BUG_ON(!chip)) + return -EINVAL; + + if (cur_val == new_val) + return 0; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto err; + } + + /* Prepare for magic command to toggle clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + + /* 2 possibilities: Internal -> send sample rate + * S/PDIF sync -> send zeroes + * NB: Sample rate locked to 48kHz on purpose to + * prevent user from resetting the sample rate + * while S/PDIF sync is enabled and confusing + * this configuration. + */ + if (new_val == 0) { + buff[0] = 0x80; + buff[1] = 0xbb; + buff[2] = 0x00; + } else { + buff[0] = buff[1] = buff[2] = 0x00; + } + + /* Send the magic command to toggle the clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x1, + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3); + if (err < 0) + goto err; + kctl->private_value = new_val; + +err: + up_read(&chip->shutdown_rwsem); + return err < 0 ? err : 1; +} + +static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[2] = { + "Internal", + "S/PDIF" + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static struct snd_kcontrol_new snd_mbox1_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_mbox1_switch_info, + .get = snd_mbox1_switch_get, + .put = snd_mbox1_switch_put, + .private_value = 0 +}; + +static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer) +{ + return snd_ctl_add(mixer->chip->card, + snd_ctl_new1(&snd_mbox1_switch, mixer)); +} + /* Native Instruments device quirks */ #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) @@ -1632,6 +1757,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_microii_controls_create(mixer); break; + case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */ + err = snd_mbox1_create_sync_switch(mixer); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, -- cgit v0.10.2 From c63fcb9b67777b906c4515a868afbd96bae4e799 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Wed, 12 Nov 2014 01:09:55 +1100 Subject: ALSA: usb-audio: Add duplex mode for Digidesign Mbox 1 and enable mixer This patch provides duplex support for the Digidesign Mbox 1 sound card and has been a work in progress for about a year. Users have confirmed on my website that previous versions of this patch have worked on the hardware and I have been testing extensively. It also enables the mixer control for providing clock source selector based on the previous patch. The sample rate has been hardcoded to 48kHz because it works better with the S/PDIF sync mode when the sample rate is locked. This is the highest rate that the device supports and no loss of functionality is observed by restricting the sample rate apart from the inability to selec a lower rate. Signed-off-by: Damien Zammit Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c657752..13f44fd 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2944,7 +2944,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = (const struct snd_usb_audio_quirk[]){ { .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE, + .type = QUIRK_AUDIO_STANDARD_MIXER, }, { .ifnum = 1, @@ -2955,16 +2955,40 @@ YAMAHA_DEVICE(0x7010, "UB99"), .iface = 1, .altsetting = 1, .altset_idx = 1, - .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .attributes = 0x4, .endpoint = 0x02, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 44100, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, .rate_max = 48000, - .nr_rates = 2, + .nr_rates = 1, .rate_table = (unsigned int[]) { - 44100, 48000 + 48000 + } + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3BE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0x4, + .endpoint = 0x81, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { + 48000 } } }, @@ -2972,7 +2996,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = -1 } } - } }, -- cgit v0.10.2 From cfd9167af14eb4ec21517a32911d460083ee3d59 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 11 Nov 2014 14:28:47 +0100 Subject: rt2x00: do not align payload on modern H/W RT2800 and newer hardware require padding between header and payload if header length is not multiple of 4. For historical reasons we also align payload to to 4 bytes boundary, but such alignment is not needed on modern H/W. Patch fixes skb_under_panic problems reported from time to time: https://bugzilla.kernel.org/show_bug.cgi?id=84911 https://bugzilla.kernel.org/show_bug.cgi?id=72471 http://marc.info/?l=linux-wireless&m=139108549530402&w=2 https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1087591 Panic happened because we eat 4 bytes of skb headroom on each (re)transmission when sending frame without the payload and the header length not being multiple of 4 (i.e. QoS header has 26 bytes). On such case because paylad_aling=2 is bigger than header_align=0 we increase header_align by 4 bytes. To prevent that we could change the check to: if (payload_length && payload_align > header_align) header_align += 4; but not aligning payload at all is more effective and alignment is not really needed by H/W (that has been tested on OpenWrt project for few years now). Reported-and-tested-by: Antti S. Lankila Debugged-by: Antti S. Lankila Reported-by: Henrik Asp Originally-From: Helmut Schaa Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 8e68f87..66ff364 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb) skb_trim(skb, frame_length); } -void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) +/* + * H/W needs L2 padding between the header and the paylod if header size + * is not 4 bytes aligned. + */ +void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len) { - unsigned int payload_length = skb->len - header_length; - unsigned int header_align = ALIGN_SIZE(skb, 0); - unsigned int payload_align = ALIGN_SIZE(skb, header_length); - unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0; + unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; - /* - * Adjust the header alignment if the payload needs to be moved more - * than the header. - */ - if (payload_align > header_align) - header_align += 4; - - /* There is nothing to do if no alignment is needed */ - if (!header_align) + if (!l2pad) return; - /* Reserve the amount of space needed in front of the frame */ - skb_push(skb, header_align); - - /* - * Move the header. - */ - memmove(skb->data, skb->data + header_align, header_length); - - /* Move the payload, if present and if required */ - if (payload_length && payload_align) - memmove(skb->data + header_length + l2pad, - skb->data + header_length + l2pad + payload_align, - payload_length); - - /* Trim the skb to the correct size */ - skb_trim(skb, header_length + l2pad + payload_length); + skb_push(skb, l2pad); + memmove(skb->data, skb->data + l2pad, hdr_len); } -void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length) +void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len) { - /* - * L2 padding is only present if the skb contains more than just the - * IEEE 802.11 header. - */ - unsigned int l2pad = (skb->len > header_length) ? - L2PAD_SIZE(header_length) : 0; + unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; if (!l2pad) return; - memmove(skb->data + l2pad, skb->data, header_length); + memmove(skb->data + l2pad, skb->data, hdr_len); skb_pull(skb, l2pad); } -- cgit v0.10.2 From 0cd75b19899fd86b51a6480fb8c00dcd85a54591 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 11 Nov 2014 13:58:44 +0100 Subject: brcmfmac: fix conversion of channel width 20MHZ_NOHT The function chandef_to_chanspec() failed when converting a chandef with bandwidth set to NL80211_CHAN_WIDTH_20_NOHT. This was reported by user running the device in AP mode. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 304 at drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c:381 chandef_to_chanspec.isra.11+0x158/0x184() Modules linked in: CPU: 0 PID: 304 Comm: hostapd Not tainted 3.16.0-rc7-abb+g64aa90f #8 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x6c/0x8c) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (chandef_to_chanspec.isra.11+0x158/0x184) [] (chandef_to_chanspec.isra.11) from [] (brcmf_cfg80211_start_ap+0x1e4/0x614) [] (brcmf_cfg80211_start_ap) from [] (nl80211_start_ap+0x288/0x414) [] (nl80211_start_ap) from [] (genl_rcv_msg+0x21c/0x38c) [] (genl_rcv_msg) from [] (netlink_rcv_skb+0xac/0xc0) [] (netlink_rcv_skb) from [] (genl_rcv+0x20/0x34) [] (genl_rcv) from [] (netlink_unicast+0x150/0x20c) [] (netlink_unicast) from [] (netlink_sendmsg+0x2b8/0x398) [] (netlink_sendmsg) from [] (sock_sendmsg+0x84/0xa8) [] (sock_sendmsg) from [] (___sys_sendmsg.part.29+0x268/0x278) [] (___sys_sendmsg.part.29) from [] (__sys_sendmsg+0x4c/0x7c) [] (__sys_sendmsg) from [] (ret_fast_syscall+0x0/0x44) ---[ end trace 965ee2158c9905a2 ]--- Cc: stable@vger.kernel.org # v3.17 Reported-by: Pontus Fuchs Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 28fa25b..39b45c0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -299,6 +299,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, primary_offset = ch->center_freq1 - ch->chan->center_freq; switch (ch->width) { case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: ch_inf.bw = BRCMU_CHAN_BW_20; WARN_ON(primary_offset != 0); break; @@ -323,6 +324,10 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, ch_inf.sb = BRCMU_CHAN_SB_LU; } break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: default: WARN_ON_ONCE(1); } @@ -333,6 +338,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, case IEEE80211_BAND_5GHZ: ch_inf.band = BRCMU_CHAN_BAND_5G; break; + case IEEE80211_BAND_60GHZ: default: WARN_ON_ONCE(1); } -- cgit v0.10.2 From 4e6ce4dc7ce71d0886908d55129d5d6482a27ff9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Thu, 6 Nov 2014 10:52:23 +0530 Subject: ath9k: Fix RTC_DERIVED_CLK usage Based on the reference clock, which could be 25MHz or 40MHz, AR_RTC_DERIVED_CLK is programmed differently for AR9340 and AR9550. But, when a chip reset is done, processing the initvals sets the register back to the default value. Fix this by moving the code in ath9k_hw_init_pll() to ar9003_hw_override_ini(). Also, do this override for AR9531. Cc: stable@vger.kernel.org Signed-off-by: Miaoqing Pan Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 697c4ae..1e8ea5e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -664,6 +664,19 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) ah->enabled_cals |= TX_CL_CAL; else ah->enabled_cals &= ~TX_CL_CAL; + + if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + if (ah->is_clk_25mhz) { + REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1); + REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); + REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); + } else { + REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1); + REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); + REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); + } + udelay(100); + } } static void ar9003_hw_prog_ini(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8be4b14..2ad6057 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -861,19 +861,6 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, udelay(RTC_PLL_SETTLE_DELAY); REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK); - - if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { - if (ah->is_clk_25mhz) { - REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1); - REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); - REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); - } else { - REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1); - REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); - REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); - } - udelay(100); - } } static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, -- cgit v0.10.2 From 45977fe35bf014f5cf9552717e1320783398ae0d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 11 Nov 2014 14:33:36 +0100 Subject: irqchip: atmel-aic: Fix irqdomain initialization First of all IRQCHIP_SKIP_SET_WAKE is not a valid irq_gc_flags and thus should not be passed as the last argument of irq_alloc_domain_generic_chips. Then pass the correct handler (handle_fasteoi_irq) to irq_alloc_domain_generic_chips instead of manually re-setting it in the initialization loop. And eventually initialize default irq flags to the pseudo standard: IRQ_REQUEST | IRQ_PROBE | IRQ_AUTOEN. Signed-off-by: Boris Brezillon Tested-by: Kevin Hilman Fixes: b1479ebb77200 ("irqchip: atmel-aic: Add atmel AIC/AIC5 drivers") Cc: # v3.17+ Link: https://lkml.kernel.org/r/1415712816-9202-1-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 6ae3cdee..cc4f9d8 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -217,8 +217,9 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, } ret = irq_alloc_domain_generic_chips(domain, 32, 1, name, - handle_level_irq, 0, 0, - IRQCHIP_SKIP_SET_WAKE); + handle_fasteoi_irq, + IRQ_NOREQUEST | IRQ_NOPROBE | + IRQ_NOAUTOEN, 0, 0); if (ret) goto err_domain_remove; @@ -230,7 +231,6 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, gc->unused = 0; gc->wake_enabled = ~0; gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK; - gc->chip_types[0].handler = handle_fasteoi_irq; gc->chip_types[0].chip.irq_eoi = irq_gc_eoi; gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown; -- cgit v0.10.2 From b207422bb92f17f4f892a9b8737d44b37fece25b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 6 Nov 2014 12:52:06 +0100 Subject: ARM: shmobile: kzm9g legacy: Set i2c clks_per_count to 2 On sh73a0/kzm9g-legacy, probing of the i2c masters fails with: i2c-sh_mobile i2c-sh_mobile.0: timing values out of range: L/H=0x208/0x1bf sh_mobile: probe of i2c-sh_mobile.0 failed with error -22 According to the datasheet, the transfer rate is derived from the HP clock (which runs at 104 MHz) divided by two. Hence i2c_sh_mobile_platform_data.clks_per_count should be set to two. Now probing succeeds, and i2c works: i2c-sh_mobile i2c-sh_mobile.0: I2C adapter 0 with bus speed 100000 Hz (L/H=0x104/0xe0) Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Simon Horman diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index b7bd8e5..328657d 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -192,11 +193,18 @@ static struct resource i2c4_resources[] = { }, }; +static struct i2c_sh_mobile_platform_data i2c_platform_data = { + .clks_per_count = 2, +}; + static struct platform_device i2c0_device = { .name = "i2c-sh_mobile", .id = 0, .resource = i2c0_resources, .num_resources = ARRAY_SIZE(i2c0_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, }; static struct platform_device i2c1_device = { @@ -204,6 +212,9 @@ static struct platform_device i2c1_device = { .id = 1, .resource = i2c1_resources, .num_resources = ARRAY_SIZE(i2c1_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, }; static struct platform_device i2c2_device = { @@ -211,6 +222,9 @@ static struct platform_device i2c2_device = { .id = 2, .resource = i2c2_resources, .num_resources = ARRAY_SIZE(i2c2_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, }; static struct platform_device i2c3_device = { @@ -218,6 +232,9 @@ static struct platform_device i2c3_device = { .id = 3, .resource = i2c3_resources, .num_resources = ARRAY_SIZE(i2c3_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, }; static struct platform_device i2c4_device = { @@ -225,6 +242,9 @@ static struct platform_device i2c4_device = { .id = 4, .resource = i2c4_resources, .num_resources = ARRAY_SIZE(i2c4_resources), + .dev = { + .platform_data = &i2c_platform_data, + }, }; static const struct sh_dmae_slave_config sh73a0_dmae_slaves[] = { -- cgit v0.10.2 From 50656d9df63d69ce399c8be62d4473b039dac36a Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Tue, 4 Nov 2014 16:37:40 -0800 Subject: ipvs: Keep skb->sk when allocating headroom on tunnel xmit ip_vs_prepare_tunneled_skb() ignores ->sk when allocating a new skb, either unconditionally setting ->sk to NULL or allowing the uninitialized ->sk from a newly allocated skb to leak through to the caller. This patch properly copies ->sk and increments its reference count. Signed-off-by: Calvin Owens Acked-by: Julian Anastasov Signed-off-by: Simon Horman diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 437a366..bd90bf8 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -846,6 +846,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af, new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) goto error; + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); consume_skb(skb); skb = new_skb; } -- cgit v0.10.2 From 1f9cd915b64bb95f7b41667b4bf8b22f0a0a557b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 11 Nov 2014 19:35:52 +0100 Subject: dmaengine: sun6i: Fix memcpy operation The prep_memcpy call was not setting any meaningful burst and width because it was relying on the dma_slave_config was not set already. Rework the needed conversion functions, and hardcode the width and burst to use. Signed-off-by: Maxime Ripard Cc: stable@vger.kernel.org Signed-off-by: Vinod Koul diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 3aa10b3..91292f5 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -230,30 +230,25 @@ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev, readl(pchan->base + DMA_CHAN_CUR_PARA)); } -static inline int convert_burst(u32 maxburst, u8 *burst) +static inline s8 convert_burst(u32 maxburst) { switch (maxburst) { case 1: - *burst = 0; - break; + return 0; case 8: - *burst = 2; - break; + return 2; default: return -EINVAL; } - - return 0; } -static inline int convert_buswidth(enum dma_slave_buswidth addr_width, u8 *width) +static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) || (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) return -EINVAL; - *width = addr_width >> 1; - return 0; + return addr_width >> 1; } static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev, @@ -284,26 +279,25 @@ static inline int sun6i_dma_cfg_lli(struct sun6i_dma_lli *lli, struct dma_slave_config *config) { u8 src_width, dst_width, src_burst, dst_burst; - int ret; if (!config) return -EINVAL; - ret = convert_burst(config->src_maxburst, &src_burst); - if (ret) - return ret; + src_burst = convert_burst(config->src_maxburst); + if (src_burst) + return src_burst; - ret = convert_burst(config->dst_maxburst, &dst_burst); - if (ret) - return ret; + dst_burst = convert_burst(config->dst_maxburst); + if (dst_burst) + return dst_burst; - ret = convert_buswidth(config->src_addr_width, &src_width); - if (ret) - return ret; + src_width = convert_buswidth(config->src_addr_width); + if (src_width) + return src_width; - ret = convert_buswidth(config->dst_addr_width, &dst_width); - if (ret) - return ret; + dst_width = convert_buswidth(config->dst_addr_width); + if (dst_width) + return dst_width; lli->cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) | DMA_CHAN_CFG_SRC_WIDTH(src_width) | @@ -542,11 +536,10 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( { struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device); struct sun6i_vchan *vchan = to_sun6i_vchan(chan); - struct dma_slave_config *sconfig = &vchan->cfg; struct sun6i_dma_lli *v_lli; struct sun6i_desc *txd; dma_addr_t p_lli; - int ret; + s8 burst, width; dev_dbg(chan2dev(chan), "%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n", @@ -565,14 +558,21 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( goto err_txd_free; } - ret = sun6i_dma_cfg_lli(v_lli, src, dest, len, sconfig); - if (ret) - goto err_dma_free; + v_lli->src = src; + v_lli->dst = dest; + v_lli->len = len; + v_lli->para = NORMAL_WAIT; + burst = convert_burst(8); + width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES); v_lli->cfg |= DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) | DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) | DMA_CHAN_CFG_DST_LINEAR_MODE | - DMA_CHAN_CFG_SRC_LINEAR_MODE; + DMA_CHAN_CFG_SRC_LINEAR_MODE | + DMA_CHAN_CFG_SRC_BURST(burst) | + DMA_CHAN_CFG_SRC_WIDTH(width) | + DMA_CHAN_CFG_DST_BURST(burst) | + DMA_CHAN_CFG_DST_WIDTH(width); sun6i_dma_lli_add(NULL, v_lli, p_lli, txd); @@ -580,8 +580,6 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( return vchan_tx_prep(&vchan->vc, &txd->vd, flags); -err_dma_free: - dma_pool_free(sdev->pool, v_lli, p_lli); err_txd_free: kfree(txd); return NULL; @@ -915,6 +913,7 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.device_prep_dma_memcpy = sun6i_dma_prep_dma_memcpy; sdc->slave.device_control = sun6i_dma_control; sdc->slave.chancnt = NR_MAX_VCHANS; + sdc->slave.copy_align = 4; sdc->slave.dev = &pdev->dev; -- cgit v0.10.2 From 2daf1b4d18e3add229d1a3b5c554331d99ac6c7e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Nov 2014 18:48:33 +0100 Subject: netfilter: nft_compat: use current net namespace Instead of init_net when using xtables over nftables compat. Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 9d6d6f6..b92f129 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -117,7 +117,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, struct xt_target *target, void *info, union nft_entry *entry, u8 proto, bool inv) { - par->net = &init_net; + par->net = ctx->net; par->table = ctx->table->name; switch (ctx->afi->family) { case AF_INET: @@ -324,7 +324,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, struct xt_match *match, void *info, union nft_entry *entry, u8 proto, bool inv) { - par->net = &init_net; + par->net = ctx->net; par->table = ctx->table->name; switch (ctx->afi->family) { case AF_INET: -- cgit v0.10.2 From c918687f5e3962375a19de6ded3c1be85ebdbcd6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 10 Nov 2014 20:53:55 +0100 Subject: netfilter: nft_compat: relax chain type validation Check for nat chain dependency only, which is the one that can actually crash the kernel. Don't care if mangle, filter and security specific match and targets are used out of their scope, they are harmless. This restores iptables-compat with mangle specific match/target when used out of the OUTPUT chain, that are actually emulated through filter chains, which broke when performing strict validation. Fixes: f3f5dde ("netfilter: nft_compat: validate chain type in match/target") Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index b92f129..70dc965 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -21,45 +21,17 @@ #include #include -static const struct { - const char *name; - u8 type; -} table_to_chaintype[] = { - { "filter", NFT_CHAIN_T_DEFAULT }, - { "raw", NFT_CHAIN_T_DEFAULT }, - { "security", NFT_CHAIN_T_DEFAULT }, - { "mangle", NFT_CHAIN_T_ROUTE }, - { "nat", NFT_CHAIN_T_NAT }, - { }, -}; - -static int nft_compat_table_to_chaintype(const char *table) -{ - int i; - - for (i = 0; table_to_chaintype[i].name != NULL; i++) { - if (strcmp(table_to_chaintype[i].name, table) == 0) - return table_to_chaintype[i].type; - } - - return -1; -} - static int nft_compat_chain_validate_dependency(const char *tablename, const struct nft_chain *chain) { - enum nft_chain_type type; const struct nft_base_chain *basechain; if (!tablename || !(chain->flags & NFT_BASE_CHAIN)) return 0; - type = nft_compat_table_to_chaintype(tablename); - if (type < 0) - return -EINVAL; - basechain = nft_base_chain(chain); - if (basechain->type->type != type) + if (strcmp(tablename, "nat") == 0 && + basechain->type->type != NFT_CHAIN_T_NAT) return -EINVAL; return 0; -- cgit v0.10.2 From afefb6f928ed42d5db452ee9251ce6de62673c67 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 10 Nov 2014 19:08:21 +0100 Subject: netfilter: nft_compat: use the match->table to validate dependencies Instead of the match->name, which is of course not relevant. Fixes: f3f5dde ("netfilter: nft_compat: validate chain type in match/target") Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 70dc965..265e190 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -346,7 +346,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, union nft_entry e = {}; int ret; - ret = nft_compat_chain_validate_dependency(match->name, ctx->chain); + ret = nft_compat_chain_validate_dependency(match->table, ctx->chain); if (ret < 0) goto err; @@ -420,7 +420,7 @@ static int nft_match_validate(const struct nft_ctx *ctx, if (!(hook_mask & match->hooks)) return -EINVAL; - ret = nft_compat_chain_validate_dependency(match->name, + ret = nft_compat_chain_validate_dependency(match->table, ctx->chain); if (ret < 0) return ret; -- cgit v0.10.2 From b326dd37b94e29bf6a15940f4fa66aa21a678ab1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 10 Nov 2014 21:14:12 +0100 Subject: netfilter: nf_tables: restore synchronous object release from commit/abort The existing xtables matches and targets, when used from nft_compat, may sleep from the destroy path, ie. when removing rules. Since the objects are released via call_rcu from softirq context, this results in lockdep splats and possible lockups that may be hard to reproduce. Patrick also indicated that delayed object release via call_rcu can cause us problems in the ordering of event notifications when anonymous sets are in place. So, this patch restores the synchronous object release from the commit and abort paths. This includes a call to synchronize_rcu() to make sure that no packets are walking on the objects that are going to be released. This is slowier though, but it's simple and it resolves the aforementioned problems. This is a partial revert of c7c32e7 ("netfilter: nf_tables: defer all object release via rcu") that was introduced in 3.16 to speed up interaction with userspace. Signed-off-by: Pablo Neira Ayuso diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 845c596..3ae969e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -396,14 +396,12 @@ struct nft_rule { /** * struct nft_trans - nf_tables object update in transaction * - * @rcu_head: rcu head to defer release of transaction data * @list: used internally * @msg_type: message type * @ctx: transaction context * @data: internal information related to the transaction */ struct nft_trans { - struct rcu_head rcu_head; struct list_head list; int msg_type; struct nft_ctx ctx; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 11ab4b07..66e8425 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans) } } -/* Schedule objects for release via rcu to make sure no packets are accesing - * removed rules. - */ -static void nf_tables_commit_release_rcu(struct rcu_head *rt) +static void nf_tables_commit_release(struct nft_trans *trans) { - struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); - switch (trans->msg_type) { case NFT_MSG_DELTABLE: nf_tables_table_destroy(&trans->ctx); @@ -3612,10 +3607,11 @@ static int nf_tables_commit(struct sk_buff *skb) } } + synchronize_rcu(); + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_del(&trans->list); - trans->ctx.nla = NULL; - call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); + nf_tables_commit_release(trans); } nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); @@ -3623,13 +3619,8 @@ static int nf_tables_commit(struct sk_buff *skb) return 0; } -/* Schedule objects for release via rcu to make sure no packets are accesing - * aborted rules. - */ -static void nf_tables_abort_release_rcu(struct rcu_head *rt) +static void nf_tables_abort_release(struct nft_trans *trans) { - struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); - switch (trans->msg_type) { case NFT_MSG_NEWTABLE: nf_tables_table_destroy(&trans->ctx); @@ -3725,11 +3716,12 @@ static int nf_tables_abort(struct sk_buff *skb) } } + synchronize_rcu(); + list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list, list) { list_del(&trans->list); - trans->ctx.nla = NULL; - call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu); + nf_tables_abort_release(trans); } return 0; -- cgit v0.10.2 From ef9566a3a1c0e46dadfa6c722e8a685ac0cea081 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:06:59 -0600 Subject: Revert "ALSA: usb-audio: Add quirk for Focusrite Scarlett This reverts commit 1762a59d8e8b5e99f6f4a0f292b40f3cacb108ba. This quirk is not needed because support for the Scarlett mixers will be added. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 13f44fd..013cba8 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2667,57 +2667,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, -{ - /* - * Focusrite Scarlett 18i6 - * - * Avoid mixer creation, which otherwise fails because some of - * the interface descriptor subtypes for interface 0 are - * unknown. That should be fixed or worked-around but this at - * least allows the device to be used successfully with a DAW - * and an external mixer. See comments below about other - * ignored interfaces. - */ - USB_DEVICE(0x1235, 0x8004), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Focusrite", - .product_name = "Scarlett 18i6", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 4, - .type = QUIRK_MIDI_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Device Firmware Update) */ - .ifnum = 5, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, /* Access Music devices */ { -- cgit v0.10.2 From f41d6049d18694e8b3d938464432d0e51f671089 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:00 -0600 Subject: ALSA: usb-audio: Add private_data pointer to usb_mixer_elem_info Add a private_data pointer to usb_mixer_elem_info to allow other mixer implementations to extend the structure as necessary. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 2c7b9c9..7423f99 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -53,6 +53,7 @@ struct usb_mixer_elem_info { int cached; int cache_val[MAX_CHANNELS]; u8 initialized; + void *private_data; }; int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, -- cgit v0.10.2 From eef90451605d79a5703756505087e0ef16da9077 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:01 -0600 Subject: ALSA: usb-audio: make set_*_mix_values functions public Make the functions set_cur_mix_value and get_cur_mix_value accessible by files that include mixer.h. In addition make usb_mixer_elem_free accessible. This allows reuse of these functions by mixers that may require quirks. The following summarizes the renamed functions: - set_cur_mix_value -> snd_usb_set_cur_mix_value - get_cur_mix_value -> snd_usb_get_cur_mix_value - usb_mixer_elem_free -> snd_usb_mixer_elem_free Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e4aaa21..14e1455 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -412,7 +412,7 @@ static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, value); } -static int get_cur_mix_value(struct usb_mixer_elem_info *cval, +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int *value) { int err; @@ -497,7 +497,7 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); } -static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value) { int err; @@ -815,7 +815,7 @@ static struct usb_feature_control_info audio_feature_info[] = { }; /* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) { kfree(kctl->private_data); kctl->private_data = NULL; @@ -998,7 +998,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, else test -= cval->res; if (test < cval->min || test > cval->max || - set_cur_mix_value(cval, minchn, 0, test) || + snd_usb_set_cur_mix_value(cval, minchn, 0, test) || get_cur_mix_raw(cval, minchn, &check)) { cval->res = last_valid_res; break; @@ -1007,7 +1007,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, break; cval->res *= 2; } - set_cur_mix_value(cval, minchn, 0, saved); + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } cval->initialized = 1; @@ -1086,7 +1086,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &val); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = get_relative_value(cval, val); @@ -1096,7 +1096,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, return 0; } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &val); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = get_relative_value(cval, val); @@ -1118,26 +1118,26 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &oval); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { - set_cur_mix_value(cval, c + 1, cnt, val); + snd_usb_set_cur_mix_value(cval, c + 1, cnt, val); changed = 1; } cnt++; } } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &oval); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) return cval->mixer->ignore_ctl_error ? 0 : err; val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { - set_cur_mix_value(cval, 0, 0, val); + snd_usb_set_cur_mix_value(cval, 0, 0, val); changed = 1; } } @@ -1250,7 +1250,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, /* * If all channels in the mask are marked read-only, make the control - * read-only. set_cur_mix_value() will check the mask again and won't + * read-only. snd_usb_set_cur_mix_value() will check the mask again and won't * issue write commands to read-only channels. */ if (cval->channels == readonly_mask) @@ -1263,7 +1263,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; @@ -1546,7 +1546,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) @@ -1846,7 +1846,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, kfree(cval); return -ENOMEM; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) { /* nothing */ ; @@ -2526,7 +2526,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) if (!(cval->cmask & (1 << c))) continue; if (cval->cached & (1 << c)) { - err = set_cur_mix_value(cval, c + 1, idx, + err = snd_usb_set_cur_mix_value(cval, c + 1, idx, cval->cache_val[idx]); if (err < 0) return err; @@ -2536,7 +2536,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) } else { /* master */ if (cval->cached) { - err = set_cur_mix_value(cval, 0, 0, *cval->cache_val); + err = snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); if (err < 0) return err; } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 7423f99..2478a84 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -76,4 +76,12 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume); #endif +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, + int index, int value); + +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, + int channel, int index, int *value); + +extern void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl); + #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4520316..b8b1f48 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -52,13 +52,6 @@ struct std_mono_table { snd_kcontrol_tlv_rw_t *tlv_callback; }; -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); - kctl->private_data = NULL; -} - /* This function allows for the creation of standard UAC controls. * See the quirks for M-Audio FTUs or Ebox-44. * If you don't want to set a TLV callback pass NULL. @@ -108,7 +101,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, /* Set name */ snprintf(kctl->id.name, sizeof(kctl->id.name), name); - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; /* set TLV */ if (tlv_callback) { -- cgit v0.10.2 From 76b188c4b370876018e3a778ec11a94a5316dbe4 Mon Sep 17 00:00:00 2001 From: Chris J Arges Date: Wed, 12 Nov 2014 12:07:02 -0600 Subject: ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20 This code contains the Scarlett mixer interface code that was originally written by Tobias Hoffman and Robin Gareus. Because the device doesn't properly implement UAC2 this code adds a mixer quirk for the device. Changes from the original code include removing the metering code along with dead code and comments. Compiler warnings were fixed. The code to initialize the sampling rate was causing a crash this was fixed as discussed on the mailing list. Error, and info messages were convered to dev_err and dev_info interfaces. The custom scarlett_mixer_elem_info struct was replaced with the more generic usb_mixer_elem_info to be able to recycle more code from mixer.c. This patch also makes additional modifications based on upstream comments. Individual control creation functions are removed and a generic function is no used. Macros for function calls are removed to improve readability. Hardcoded control initialization is removed. Save to HW functionality has been removed. Strings for enums are created dynamically for the mixer. Strings used for controls are now SNDRV_CTL_ELEM_ID_NAME_MAXLEN length. Signed-off-by: Chris J Arges Signed-off-by: Takashi Iwai diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 2b92f0d..bcee406 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -9,6 +9,7 @@ snd-usb-audio-objs := card.o \ helper.o \ mixer.o \ mixer_quirks.o \ + mixer_scarlett.o \ pcm.o \ proc.o \ quirks.o \ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index b8b1f48..7b9331a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -41,6 +41,7 @@ #include "usbaudio.h" #include "mixer.h" #include "mixer_quirks.h" +#include "mixer_scarlett.h" #include "helper.h" extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; @@ -1770,6 +1771,14 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) /* detection is disabled in mixer_maps.c */ err = snd_create_std_mono_table(mixer, ebox44_table); break; + + case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */ + case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */ + case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */ + case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */ + case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */ + err = snd_scarlett_controls_create(mixer); + break; } return err; diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c new file mode 100644 index 0000000..a0a8745 --- /dev/null +++ b/sound/usb/mixer_scarlett.c @@ -0,0 +1,967 @@ +/* + * Scarlett Driver for ALSA + * + * Copyright (c) 2013 by Tobias Hoffmann + * Copyright (c) 2013 by Robin Gareus + * Copyright (c) 2002 by Takashi Iwai + * Copyright (c) 2014 by Chris J Arges + * + * Many codes borrowed from audio.c by + * Alan Cox (alan at lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer at ife.ee.ethz.ch) + * + * Code cleanup: + * David Henningsson + * + * 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. + * + */ + +/* + * Rewritten and extended to support more models, e.g. Scarlett 18i8. + * + * Auto-detection via UAC2 is not feasible to properly discover the vast + * majority of features. It's related to both Linux/ALSA's UAC2 as well as + * Focusrite's implementation of it. Eventually quirks may be sufficient but + * right now it's a major headache to work arount these things. + * + * NB. Neither the OSX nor the win driver provided by Focusrite performs + * discovery, they seem to operate the same as this driver. + */ + +/* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. + * + * The protocol was reverse engineered by looking at communication between + * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 + * (firmware v305) using wireshark and usbmon in January 2013. + * Extended in July 2013. + * + * this mixer gives complete access to all features of the device: + * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) + * - select clock source + * - dynamic input to mixer-matrix assignment + * - 18 x 6 mixer-matrix gain stages + * - bus routing & volume control + * - automatic re-initialization on connect if device was power-cycled + * + * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) + * wIndex + * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + + * channel, data=Line/Inst (2bytes) + * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) + * ?? wValue=0x0803/04, ?? (2bytes) + * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) + * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) + * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) + * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) + * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) + * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) + * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) + * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) + * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) + * + * USB reads: (i.e. actually issued by original software) + * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) + * 0x29 wValue=0x0100 sample-rate(4bytes) + * wValue=0x0200 ?? 1byte (only once) + * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? + * + * USB reads with bRequest = 0x03 = UAC2_CS_MEM + * 0x3c wValue=0x0002 1byte: sync status (locked=1) + * wValue=0x0000 18*2byte: peak meter (inputs) + * wValue=0x0001 8(?)*2byte: peak meter (mix) + * wValue=0x0003 6*2byte: peak meter (pcm/daw) + * + * USB write with bRequest = 0x03 + * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 + * + * + * + * /--------------\ 18chn 6chn /--------------\ + * | Hardware in +--+-------\ /------+--+ ALSA PCM out | + * \--------------/ | | | | \--------------/ + * | | | | + * | v v | + * | +---------------+ | + * | \ Matrix Mux / | + * | +-----+-----+ | + * | | | + * | | 18chn | + * | v | + * | +-----------+ | + * | | Mixer | | + * | | Matrix | | + * | | | | + * | | 18x6 Gain | | + * | | stages | | + * | +-----+-----+ | + * | | | + * | | | + * | 18chn | 6chn | 6chn + * v v v + * ========================= + * +---------------+ +--—------------+ + * \ Output Mux / \ Capture Mux / + * +-----+-----+ +-----+-----+ + * | | + * | 6chn | + * v | + * +-------------+ | + * | Master Gain | | + * +------+------+ | + * | | + * | 6chn | 18chn + * | (3 stereo pairs) | + * /--------------\ | | /--------------\ + * | Hardware out |<--/ \-->| ALSA PCM in | + * \--------------/ \--------------/ + * + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" +#include "power.h" + +#include "mixer_scarlett.h" + +/* some gui mixers can't handle negative ctl values */ +#define SND_SCARLETT_LEVEL_BIAS 128 +#define SND_SCARLETT_MATRIX_IN_MAX 18 +#define SND_SCARLETT_CONTROLS_MAX 10 +#define SND_SCARLETT_OFFSETS_MAX 5 + +enum { + SCARLETT_OUTPUTS, + SCARLETT_SWITCH_IMPEDANCE, + SCARLETT_SWITCH_PAD, +}; + +enum { + SCARLETT_OFFSET_PCM = 0, + SCARLETT_OFFSET_ANALOG = 1, + SCARLETT_OFFSET_SPDIF = 2, + SCARLETT_OFFSET_ADAT = 3, + SCARLETT_OFFSET_MIX = 4, +}; + +struct scarlett_mixer_elem_enum_info { + int start; + int len; + int offsets[SND_SCARLETT_OFFSETS_MAX]; + char const * const *names; +}; + +struct scarlett_mixer_control { + unsigned char num; + unsigned char type; + const char *name; +}; + +struct scarlett_device_info { + int matrix_in; + int matrix_out; + int input_len; + int output_len; + + struct scarlett_mixer_elem_enum_info opt_master; + struct scarlett_mixer_elem_enum_info opt_matrix; + + /* initial values for matrix mux */ + int matrix_mux_init[SND_SCARLETT_MATRIX_IN_MAX]; + + int num_controls; /* number of items in controls */ + const struct scarlett_mixer_control controls[SND_SCARLETT_CONTROLS_MAX]; +}; + +/********************** Enum Strings *************************/ + +static const struct scarlett_mixer_elem_enum_info opt_pad = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "0dB", "-10dB" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_impedance = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "Line", "Hi-Z" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_clock = { + .start = 1, + .len = 3, + .offsets = {}, + .names = (char const * const []){ + "Internal", "SPDIF", "ADAT" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_sync = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "No Lock", "Locked" + } +}; + +static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int scarlett_ctl_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = !val; /* invert mute logic for mixer */ + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i]; + val = !val; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static int scarlett_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)kctl->private_value + + SND_SCARLETT_LEVEL_BIAS; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = clamp(val / 256, -128, (int)kctl->private_value) + + SND_SCARLETT_LEVEL_BIAS; + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i] - + SND_SCARLETT_LEVEL_BIAS; + val = val * 256; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static void scarlett_generate_name(int i, char *dst, int offsets[]) +{ + if (i > offsets[SCARLETT_OFFSET_MIX]) + sprintf(dst, "Mix %c", + 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); + else if (i > offsets[SCARLETT_OFFSET_ADAT]) + sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); + else if (i > offsets[SCARLETT_OFFSET_SPDIF]) + sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); + else if (i > offsets[SCARLETT_OFFSET_ANALOG]) + sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); + else if (i > offsets[SCARLETT_OFFSET_PCM]) + sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); + else + sprintf(dst, "Off"); +} + +static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + unsigned int items = opt->len; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = elem->channels; + uinfo->value.enumerated.items = items; + + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + /* generate name dynamically based on item number and offset info */ + scarlett_generate_name(uinfo->value.enumerated.item, + uinfo->value.enumerated.name, + opt->offsets); + + return 0; +} + +static int scarlett_ctl_enum_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + + return snd_ctl_enum_info(uinfo, elem->channels, opt->len, + (const char * const *)opt->names); +} + +static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &val); + if (err < 0) + return err; + + val = clamp(val - opt->start, 0, opt->len-1); + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, oval, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[0]; + val = val + opt->start; + if (val != oval) { + snd_usb_set_cur_mix_value(elem, 0, 0, val); + return 1; + } + return 0; +} + +static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct snd_usb_audio *chip = elem->mixer->chip; + unsigned char buf[2 * MAX_CHANNELS] = {0, }; + int wValue = (elem->control << 8) | elem->idx_off; + int idx = snd_usb_ctrl_intf(chip) | (elem->id << 8); + int err; + + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC2_CS_MEM, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_IN, wValue, idx, buf, elem->channels); + if (err < 0) + return err; + + ucontrol->value.enumerated.item[0] = clamp((int)buf[0], 0, 1); + return 0; +} + +static struct snd_kcontrol_new usb_scarlett_ctl_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_switch_info, + .get = scarlett_ctl_switch_get, + .put = scarlett_ctl_switch_put, +}; + +static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0); + +static struct snd_kcontrol_new usb_scarlett_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_master = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_dynamic_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_sync = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_meter_get, +}; + +static int add_new_ctl(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *ncontrol, + int index, int offset, int num, + int val_type, int channels, const char *name, + const struct scarlett_mixer_elem_enum_info *opt, + struct usb_mixer_elem_info **elem_ret +) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + int err; + + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + elem->mixer = mixer; + elem->control = offset; + elem->idx_off = num; + elem->id = index; + elem->val_type = val_type; + + elem->channels = channels; + + /* add scarlett_mixer_elem_enum_info struct */ + elem->private_data = (void *)opt; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); + + err = snd_ctl_add(mixer->chip->card, kctl); + if (err < 0) + return err; + + if (elem_ret) + *elem_ret = elem; + + return 0; +} + +static int add_output_ctls(struct usb_mixer_interface *mixer, + int index, const char *name, + const struct scarlett_device_info *info) +{ + int err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct usb_mixer_elem_info *elem; + + /* Add mute switch */ + snprintf(mx, sizeof(mx), "Master %d (%s) Playback Switch", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* Add volume control and initialize to 0 */ + snprintf(mx, sizeof(mx), "Master %d (%s) Playback Volume", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* Add L channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + /* Add R channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + return 0; +} + +/********************** device-specific config *************************/ + +/* untested... */ +static struct scarlett_device_info s6i6_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 6, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 27, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 0, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +/* untested... */ +static struct scarlett_device_info s8i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 8, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 25, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 7, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +static struct scarlett_device_info s18i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 18, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 31, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .opt_matrix = { + .start = -1, + .len = 25, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .num_controls = 5, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + }, + + .matrix_mux_init = { + 6, 7, 8, 9, 10, 11, 12, 13, /* Analog -> 1..8 */ + 16, 17, 18, 19, 20, 21, /* ADAT[1..6] -> 9..14 */ + 14, 15, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i8_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 8, + + .opt_master = { + .start = -1, + .len = 35, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 27, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone 1" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Headphone 2" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 8, 9, 10, 11, 12, 13, 14, 15, /* Analog -> 1..8 */ + 18, 19, 20, 21, 22, 23, /* ADAT[1..6] -> 9..14 */ + 16, 17, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i20_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 20, + + .opt_master = { + .start = -1, + .len = 47, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 39, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Line 3/4" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Line 5/6" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "Line 7/8" }, + { .num = 4, .type = SCARLETT_OUTPUTS, .name = "Line 9/10" }, + { .num = 5, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 6, .type = SCARLETT_OUTPUTS, .name = "ADAT 1/2" }, + { .num = 7, .type = SCARLETT_OUTPUTS, .name = "ADAT 3/4" }, + { .num = 8, .type = SCARLETT_OUTPUTS, .name = "ADAT 5/6" }, + { .num = 9, .type = SCARLETT_OUTPUTS, .name = "ADAT 7/8" }, + /*{ .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL},*/ + }, + + .matrix_mux_init = { + 20, 21, 22, 23, 24, 25, 26, 27, /* Analog -> 1..8 */ + 30, 31, 32, 33, 34, 35, /* ADAT[1..6] -> 9..14 */ + 28, 29, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + + +static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, + struct scarlett_device_info *info) +{ + int i, err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const struct scarlett_mixer_control *ctl; + struct usb_mixer_elem_info *elem; + + /* create master switch and playback volume */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, 0, + USB_MIXER_S16, 1, "Master Playback Switch", NULL, + &elem); + if (err < 0) + return err; + + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, 0, + USB_MIXER_S16, 1, "Master Playback Volume", NULL, + &elem); + if (err < 0) + return err; + + /* iterate through controls in info struct and create each one */ + for (i = 0; i < info->num_controls; i++) { + ctl = &info->controls[i]; + + switch (ctl->type) { + case SCARLETT_OUTPUTS: + err = add_output_ctls(mixer, ctl->num, ctl->name, info); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_IMPEDANCE: + sprintf(mx, "Input %d Impedance Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + 0x09, ctl->num, USB_MIXER_S16, 1, mx, + &opt_impedance, &elem); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_PAD: + sprintf(mx, "Input %d Pad Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + 0x0b, ctl->num, USB_MIXER_S16, 1, mx, + &opt_pad, &elem); + if (err < 0) + return err; + break; + } + } + + return 0; +} + +/* + * Create and initialize a mixer for the Focusrite(R) Scarlett + */ +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct scarlett_device_info *info; + struct usb_mixer_elem_info *elem; + static char sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' }; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + switch (mixer->chip->usb_id) { + case USB_ID(0x1235, 0x8012): + info = &s6i6_info; + break; + case USB_ID(0x1235, 0x8002): + info = &s8i6_info; + break; + case USB_ID(0x1235, 0x8004): + info = &s18i6_info; + break; + case USB_ID(0x1235, 0x8014): + info = &s18i8_info; + break; + case USB_ID(0x1235, 0x800c): + info = &s18i20_info; + break; + default: /* device not (yet) supported */ + return -EINVAL; + } + + /* generic function to create controls */ + err = scarlett_controls_create_generic(mixer, info); + if (err < 0) + return err; + + /* setup matrix controls */ + for (i = 0; i < info->matrix_in; i++) { + snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", + i+1); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x32, + 0x06, i, USB_MIXER_S16, 1, mx, + &info->opt_matrix, &elem); + if (err < 0) + return err; + + for (o = 0; o < info->matrix_out; o++) { + sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, + o+'A'); + err = add_new_ctl(mixer, &usb_scarlett_ctl, 0x3c, 0x00, + (i << 3) + (o & 0x07), USB_MIXER_S16, + 1, mx, NULL, &elem); + if (err < 0) + return err; + + } + } + + for (i = 0; i < info->input_len; i++) { + snprintf(mx, sizeof(mx), "Input Source %02d Capture Route", + i+1); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x34, + 0x00, i, USB_MIXER_S16, 1, mx, + &info->opt_master, &elem); + if (err < 0) + return err; + } + + /* val_len == 1 needed here */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x28, 0x01, 0, + USB_MIXER_U8, 1, "Sample Clock Source", + &opt_clock, &elem); + if (err < 0) + return err; + + /* val_len == 1 and UAC2_CS_MEM */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, 0x3c, 0x00, 2, + USB_MIXER_U8, 1, "Sample Clock Sync Status", + &opt_sync, &elem); + if (err < 0) + return err; + + /* initialize sampling rate to 48000 */ + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->chip) | + (0x29 << 8), sample_rate_buffer, 4); + if (err < 0) + return err; + + return err; +} diff --git a/sound/usb/mixer_scarlett.h b/sound/usb/mixer_scarlett.h new file mode 100644 index 0000000..19c592a --- /dev/null +++ b/sound/usb/mixer_scarlett.h @@ -0,0 +1,6 @@ +#ifndef __USB_MIXER_SCARLETT_H +#define __USB_MIXER_SCARLETT_H + +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer); + +#endif /* __USB_MIXER_SCARLETT_H */ -- cgit v0.10.2 From 121a2f6d5f09d929fc663349ba34e248e8d07391 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Mon, 3 Nov 2014 23:20:04 +0100 Subject: ARM: tegra: Add serial port labels to Tegra124 DT These labels will be used to provide deterministic numbering of consoles in a later patch. Signed-off-by: Lucas Stach [treding@nvidia.com: drop aliases, reword commit message] Signed-off-by: Thierry Reding diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi index 478c555..df2b06b 100644 --- a/arch/arm/boot/dts/tegra124.dtsi +++ b/arch/arm/boot/dts/tegra124.dtsi @@ -286,7 +286,7 @@ * the APB DMA based serial driver, the comptible is * "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart". */ - serial@0,70006000 { + uarta: serial@0,70006000 { compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart"; reg = <0x0 0x70006000 0x0 0x40>; reg-shift = <2>; @@ -299,7 +299,7 @@ status = "disabled"; }; - serial@0,70006040 { + uartb: serial@0,70006040 { compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart"; reg = <0x0 0x70006040 0x0 0x40>; reg-shift = <2>; @@ -312,7 +312,7 @@ status = "disabled"; }; - serial@0,70006200 { + uartc: serial@0,70006200 { compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart"; reg = <0x0 0x70006200 0x0 0x40>; reg-shift = <2>; @@ -325,7 +325,7 @@ status = "disabled"; }; - serial@0,70006300 { + uartd: serial@0,70006300 { compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart"; reg = <0x0 0x70006300 0x0 0x40>; reg-shift = <2>; -- cgit v0.10.2 From c4574aa00e7c144ae4d1bfc2388433d9eb82e4d3 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 11 Nov 2014 12:49:30 -0800 Subject: ARM: dts: tegra: move serial aliases to per-board There are general changes pending to make the /aliases/serial* entries number the serial ports on the system. On Tegra, so far the ports have been just numbered dynamically as they are configured so that makes them change. To avoid this, add specific aliases per board to keep the old numbers. This allows us to change the numbering by default on future SoCs while keeping the numbering on existing boards. Signed-off-by: Olof Johansson Signed-off-by: Thierry Reding diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts index 5c21d21..8b7aa0d 100644 --- a/arch/arm/boot/dts/tegra114-dalmore.dts +++ b/arch/arm/boot/dts/tegra114-dalmore.dts @@ -15,6 +15,7 @@ aliases { rtc0 = "/i2c@7000d000/tps65913@58"; rtc1 = "/rtc@7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra114-roth.dts b/arch/arm/boot/dts/tegra114-roth.dts index c7c6825..a80f1e2 100644 --- a/arch/arm/boot/dts/tegra114-roth.dts +++ b/arch/arm/boot/dts/tegra114-roth.dts @@ -15,6 +15,10 @@ linux,initrd-end = <0x82800000>; }; + aliases { + serial0 = &uartd; + }; + firmware { trusted-foundations { compatible = "tlm,trusted-foundations"; diff --git a/arch/arm/boot/dts/tegra114-tn7.dts b/arch/arm/boot/dts/tegra114-tn7.dts index 9636621..2301c66 100644 --- a/arch/arm/boot/dts/tegra114-tn7.dts +++ b/arch/arm/boot/dts/tegra114-tn7.dts @@ -15,6 +15,10 @@ linux,initrd-end = <0x82800000>; }; + aliases { + serial0 = &uartd; + }; + firmware { trusted-foundations { compatible = "tlm,trusted-foundations"; diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index 2ca9c18..222f3b3 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -9,13 +9,6 @@ compatible = "nvidia,tegra114"; interrupt-parent = <&gic>; - aliases { - serial0 = &uarta; - serial1 = &uartb; - serial2 = &uartc; - serial3 = &uartd; - }; - host1x@50000000 { compatible = "nvidia,tegra114-host1x", "simple-bus"; reg = <0x50000000 0x00028000>; diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts index 029c9a02..51b373f 100644 --- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts +++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@0,7000d000/pmic@40"; rtc1 = "/rtc@0,7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra124-nyan-big.dts b/arch/arm/boot/dts/tegra124-nyan-big.dts index 7d0784c..53181d3 100644 --- a/arch/arm/boot/dts/tegra124-nyan-big.dts +++ b/arch/arm/boot/dts/tegra124-nyan-big.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@0,7000d000/pmic@40"; rtc1 = "/rtc@0,7000e000"; + serial0 = &uarta; }; memory { diff --git a/arch/arm/boot/dts/tegra124-venice2.dts b/arch/arm/boot/dts/tegra124-venice2.dts index 1300885..5c3f781 100644 --- a/arch/arm/boot/dts/tegra124-venice2.dts +++ b/arch/arm/boot/dts/tegra124-venice2.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@0,7000d000/pmic@40"; rtc1 = "/rtc@0,7000e000"; + serial0 = &uarta; }; memory { diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index a37279a..b926a07 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@7000d000/tps6586x@34"; rtc1 = "/rtc@7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra20-iris-512.dts b/arch/arm/boot/dts/tegra20-iris-512.dts index 8cfb83f..1dd7d7b 100644 --- a/arch/arm/boot/dts/tegra20-iris-512.dts +++ b/arch/arm/boot/dts/tegra20-iris-512.dts @@ -6,6 +6,11 @@ model = "Toradex Colibri T20 512MB on Iris"; compatible = "toradex,iris", "toradex,colibri_t20-512", "nvidia,tegra20"; + aliases { + serial0 = &uarta; + serial1 = &uartd; + }; + host1x@50000000 { hdmi@54280000 { status = "okay"; diff --git a/arch/arm/boot/dts/tegra20-medcom-wide.dts b/arch/arm/boot/dts/tegra20-medcom-wide.dts index 1b7c56b..9b87526 100644 --- a/arch/arm/boot/dts/tegra20-medcom-wide.dts +++ b/arch/arm/boot/dts/tegra20-medcom-wide.dts @@ -6,6 +6,10 @@ model = "Avionic Design Medcom-Wide board"; compatible = "ad,medcom-wide", "ad,tamonten", "nvidia,tegra20"; + aliases { + serial0 = &uartd; + }; + pwm@7000a000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index d4438e3..ed7e100 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -10,6 +10,8 @@ aliases { rtc0 = "/i2c@7000d000/tps6586x@34"; rtc1 = "/rtc@7000e000"; + serial0 = &uarta; + serial1 = &uartc; }; memory { diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index a1d4bf9..ea282c7 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@7000d000/tps6586x@34"; rtc1 = "/rtc@7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi index 80e7d38..13d4e61 100644 --- a/arch/arm/boot/dts/tegra20-tamonten.dtsi +++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi @@ -7,6 +7,7 @@ aliases { rtc0 = "/i2c@7000d000/tps6586x@34"; rtc1 = "/rtc@7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index 5ad8797..d99af4e 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@7000c500/rtc@56"; rtc1 = "/rtc@7000e000"; + serial0 = &uarta; }; memory { diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index ca8484c..04c58e9 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@7000d000/tps6586x@34"; rtc1 = "/rtc@7000e000"; + serial0 = &uartd; }; memory { diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index 1843725..340d811 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -10,6 +10,7 @@ aliases { rtc0 = "/i2c@7000d000/max8907@3c"; rtc1 = "/rtc@7000e000"; + serial0 = &uarta; }; memory { diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 3b374c4..8acf5d8 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -9,14 +9,6 @@ compatible = "nvidia,tegra20"; interrupt-parent = <&intc>; - aliases { - serial0 = &uarta; - serial1 = &uartb; - serial2 = &uartc; - serial3 = &uartd; - serial4 = &uarte; - }; - host1x@50000000 { compatible = "nvidia,tegra20-host1x", "simple-bus"; reg = <0x50000000 0x00024000>; diff --git a/arch/arm/boot/dts/tegra30-apalis-eval.dts b/arch/arm/boot/dts/tegra30-apalis-eval.dts index 45d40f0..6236bde 100644 --- a/arch/arm/boot/dts/tegra30-apalis-eval.dts +++ b/arch/arm/boot/dts/tegra30-apalis-eval.dts @@ -11,6 +11,10 @@ rtc0 = "/i2c@7000c000/rtc@68"; rtc1 = "/i2c@7000d000/tps65911@2d"; rtc2 = "/rtc@7000e000"; + serial0 = &uarta; + serial1 = &uartb; + serial2 = &uartc; + serial3 = &uartd; }; pcie-controller@00003000 { diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts index cee8f22..6b157ee 100644 --- a/arch/arm/boot/dts/tegra30-beaver.dts +++ b/arch/arm/boot/dts/tegra30-beaver.dts @@ -9,6 +9,7 @@ aliases { rtc0 = "/i2c@7000d000/tps65911@2d"; rtc1 = "/rtc@7000e000"; + serial0 = &uarta; }; memory { diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi index 2063795..a1b682e 100644 --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi @@ -30,6 +30,8 @@ aliases { rtc0 = "/i2c@7000d000/tps65911@2d"; rtc1 = "/rtc@7000e000"; + serial0 = &uarta; + serial1 = &uartc; }; memory { diff --git a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts index 7793abd..4d3ddc5 100644 --- a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts +++ b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts @@ -10,6 +10,9 @@ rtc0 = "/i2c@7000c000/rtc@68"; rtc1 = "/i2c@7000d000/tps65911@2d"; rtc2 = "/rtc@7000e000"; + serial0 = &uarta; + serial1 = &uartb; + serial2 = &uartd; }; host1x@50000000 { diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index aa6ccea..b270b9e 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -9,14 +9,6 @@ compatible = "nvidia,tegra30"; interrupt-parent = <&intc>; - aliases { - serial0 = &uarta; - serial1 = &uartb; - serial2 = &uartc; - serial3 = &uartd; - serial4 = &uarte; - }; - pcie-controller@00003000 { compatible = "nvidia,tegra30-pcie"; device_type = "pci"; -- cgit v0.10.2 From edbde56a25f484d46c9e75563d3b310bde1c185e Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 13 Nov 2014 13:59:02 +0900 Subject: ARM: tegra: Remove eMMC vmmc property for roth/tn7 This property was wrong and broke eMMC since commit 52221610d ("mmc: sdhci: Improve external VDD regulator support"). Align the eMMC properties to those of other Tegra boards. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding diff --git a/arch/arm/boot/dts/tegra114-roth.dts b/arch/arm/boot/dts/tegra114-roth.dts index a80f1e2..54c79c5 100644 --- a/arch/arm/boot/dts/tegra114-roth.dts +++ b/arch/arm/boot/dts/tegra114-roth.dts @@ -975,7 +975,6 @@ sdhci@78000600 { status = "okay"; bus-width = <8>; - vmmc-supply = <&vdd_1v8>; non-removable; }; diff --git a/arch/arm/boot/dts/tegra114-tn7.dts b/arch/arm/boot/dts/tegra114-tn7.dts index 2301c66..f91c2c9 100644 --- a/arch/arm/boot/dts/tegra114-tn7.dts +++ b/arch/arm/boot/dts/tegra114-tn7.dts @@ -244,7 +244,6 @@ sdhci@78000600 { status = "okay"; bus-width = <8>; - vmmc-supply = <&vdd_1v8>; non-removable; }; -- cgit v0.10.2 From 221b9bf42b26a22e6904d20f35c53aec2e73a646 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 13 Nov 2014 13:59:03 +0900 Subject: ARM: tegra: roth: Fix SD card VDD_IO regulator vddio_sdmmc3 is a vdd_io, and thus should be under the vqmmc-supply property, not vmmc-supply. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding diff --git a/arch/arm/boot/dts/tegra114-roth.dts b/arch/arm/boot/dts/tegra114-roth.dts index 54c79c5..38acf78 100644 --- a/arch/arm/boot/dts/tegra114-roth.dts +++ b/arch/arm/boot/dts/tegra114-roth.dts @@ -920,8 +920,6 @@ regulator-name = "vddio-sdmmc3"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; - regulator-always-on; - regulator-boot-on; }; ldousb { @@ -966,7 +964,7 @@ sdhci@78000400 { status = "okay"; bus-width = <4>; - vmmc-supply = <&vddio_sdmmc3>; + vqmmc-supply = <&vddio_sdmmc3>; cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>; power-gpios = <&gpio TEGRA_GPIO(H, 0) GPIO_ACTIVE_HIGH>; }; -- cgit v0.10.2 From 336b5be2c56f3cf2252f5a39d344ef67cf9bdf7a Mon Sep 17 00:00:00 2001 From: Duc Dang Date: Thu, 6 Nov 2014 17:14:18 -0800 Subject: PCI: xgene: Assign resources to bus before adding new devices The X-Gene PCIe driver assumes pci_scan_root_bus() assigns resources as proposed in [1]. But we dropped patch [1] because it would break some architectures, which means the X-Gene PCIe driver is currently broken. Add calls to scan the bus, assign resources, and add devices in the X-Gene driver to fix this. [bhelgaas: changelog] [1] http://lkml.kernel.org/r/1412000971-9242-11-git-send-email-Liviu.Dudau@arm.com Signed-off-by: Duc Dang Signed-off-by: Tanmay Inamdar Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index 9ecabfa..2988fe1 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -631,10 +631,15 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) if (ret) return ret; - bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res); + bus = pci_create_root_bus(&pdev->dev, 0, + &xgene_pcie_ops, port, &res); if (!bus) return -ENOMEM; + pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); + platform_set_drvdata(pdev, port); return 0; } -- cgit v0.10.2 From 2801f7252d5be3f4f3886c2a5890284232801afe Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Thu, 6 Nov 2014 16:23:20 +0100 Subject: PCI: Add missing DT binding for "linux,pci-domain" property 41e5c0f81d3e ("of/pci: Add pci_get_new_domain_nr() and of_get_pci_domain_nr()") added parsing of the "linux,pci-domain" property, but didn't add the binding documentation. Since this property will be supported by a number of host bridge drivers, add it to the common PCI binding doc. Fixes: 41e5c0f81d3e ("of/pci: Add pci_get_new_domain_nr() and of_get_pci_domain_nr()") Signed-off-by: Lucas Stach Signed-off-by: Bjorn Helgaas Acked-by: Liviu Dudau Acked-by: Rob Herring diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt index 41aeed3..f8fbe9a 100644 --- a/Documentation/devicetree/bindings/pci/pci.txt +++ b/Documentation/devicetree/bindings/pci/pci.txt @@ -7,3 +7,14 @@ And for the interrupt mapping part: Open Firmware Recommended Practice: Interrupt Mapping http://www.openfirmware.org/1275/practice/imap/imap0_9d.pdf + +Additionally to the properties specified in the above standards a host bridge +driver implementation may support the following properties: + +- linux,pci-domain: + If present this property assigns a fixed PCI domain number to a host bridge, + otherwise an unstable (across boots) unique number will be assigned. + It is required to either not set this property at all or set it for all + host bridges in the system, otherwise potentially conflicting domain numbers + may be assigned to root buses behind different host bridges. The domain + number for each host bridge in the system must be unique. -- cgit v0.10.2 From 7a1562d4f2d01721ad07c3a326db7512077ceea9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 11 Nov 2014 12:09:46 -0800 Subject: PCI: Apply _HPX Link Control settings to all devices with a link Previously we applied _HPX type 2 record Link Control register settings only to bridges with a subordinate bus. But it's better to apply them to all devices with a link because if the subordinate bus has not been allocated yet, we won't apply settings to the device. Use pcie_cap_has_lnkctl() to determine whether the device has a Link Control register instead of looking at dev->subordinate. [bhelgaas: changelog] Fixes: 6cd33649fa83 ("PCI: Add pci_configure_device() during enumeration") Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/access.c b/drivers/pci/access.c index d292d7c..49dd766 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -444,7 +444,7 @@ static inline int pcie_cap_version(const struct pci_dev *dev) return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS; } -static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev) +bool pcie_cap_has_lnkctl(const struct pci_dev *dev) { int type = pci_pcie_type(dev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0601890..4a3902d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -6,6 +6,8 @@ extern const unsigned char pcie_link_speed[]; +bool pcie_cap_has_lnkctl(const struct pci_dev *dev); + /* Functions internal to the PCI core code */ int pci_create_sysfs_dev_files(struct pci_dev *pdev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5ed9930..6244b18 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1323,7 +1323,7 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or); /* Initialize Link Control Register */ - if (dev->subordinate) + if (pcie_cap_has_lnkctl(dev)) pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or); -- cgit v0.10.2 From 9547c0999e50fd624cab52f94a79f0fd27a7cb84 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Oct 2014 14:22:49 -0700 Subject: ALSA: 6fire: Convert byte_rev_table uses to bitrev8 Use the inline function instead of directly indexing the array. This allows some architectures with hardware instructions for bit reversals to eliminate the array. Signed-off-by: Joe Perches Signed-off-by: Takashi Iwai diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 3b02e54..62c25e7 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -316,7 +316,7 @@ static int usb6fire_fw_fpga_upload( while (c != end) { for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) - buffer[i] = byte_rev_table[(u8) *c]; + buffer[i] = bitrev8((u8)*c); ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { -- cgit v0.10.2 From 387417b56295ef93d7cb38e1721826c85dfe897c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 14 Nov 2014 16:09:05 +0530 Subject: ALSA: ice1712: remove unneeded return statement the functions: snd_ice1712_akm4xxx_build_controls snd_ice1712_build_pro_mixer snd_ctl_add snd_ak4114_build prodigy192_ak4114_init snd_ak4113_build are all returning either 0 or a negetive error value. so we can easily remove the check for a negative value and return the value instead. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index 59e37c5..a40001c 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -309,11 +309,7 @@ static int snd_ice1712_value_init(struct snd_ice1712 *ice) return err; /* ak4524 controls */ - err = snd_ice1712_akm4xxx_build_controls(ice); - if (err < 0) - return err; - - return 0; + return snd_ice1712_akm4xxx_build_controls(ice); } static int snd_ice1712_ez8_init(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 48a0c33..5975334 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1295,10 +1295,7 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd return err; } - err = snd_ice1712_build_pro_mixer(ice); - if (err < 0) - return err; - return 0; + return snd_ice1712_build_pro_mixer(ice); } /* @@ -1545,10 +1542,9 @@ static int snd_ice1712_ac97_mixer(struct snd_ice1712 *ice) dev_warn(ice->card->dev, "cannot initialize ac97 for consumer, skipped\n"); else { - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice)); - if (err < 0) - return err; - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, + ice)); } } @@ -2497,11 +2493,8 @@ static int snd_ice1712_build_controls(struct snd_ice1712 *ice) err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice)); if (err < 0) return err; - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); - if (err < 0) - return err; - - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); } static int snd_ice1712_free(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index f633e3b..ea53167 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2497,11 +2497,8 @@ static int snd_vt1724_build_controls(struct snd_ice1712 *ice) return err; } - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice)); - if (err < 0) - return err; - - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice)); } static int snd_vt1724_free(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 7a6c078..a1536c1 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -475,11 +475,8 @@ static int juli_add_controls(struct snd_ice1712 *ice) return err; /* only capture SPDIF over AK4114 */ - err = snd_ak4114_build(spec->ak4114, NULL, + return snd_ak4114_build(spec->ak4114, NULL, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - if (err < 0) - return err; - return 0; } /* diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 1eb151aa..3919aed 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -758,10 +758,8 @@ static int prodigy192_init(struct snd_ice1712 *ice) "AK4114 initialized with status %d\n", err); } else dev_dbg(ice->card->dev, "AK4114 not found\n"); - if (err < 0) - return err; - return 0; + return err; } diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index d4caf9d..6f55e02 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -833,11 +833,8 @@ static int qtet_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; /* only capture SPDIF over AK4113 */ - err = snd_ak4113_build(spec->ak4113, + return snd_ak4113_build(spec->ak4113, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - if (err < 0) - return err; - return 0; } static inline int qtet_is_spdif_master(struct snd_ice1712 *ice) -- cgit v0.10.2 From b393df0145e271724fee10f93c023662f8557bb9 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 14 Nov 2014 16:09:07 +0530 Subject: ALSA: ice1712: remove unused variable buf_size was initialized with snd_pcm_lib_buffer_bytes, but never used. and so it is safe to be deleted. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 5975334..6525191 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -620,10 +620,9 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream) { struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - u32 period_size, buf_size, rate, tmp, chn; + u32 period_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; -- cgit v0.10.2 From b8eca77e54525c818f35f51afb64fc13205443a3 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 14 Nov 2014 18:12:21 +0530 Subject: ALSA: ice1712: consider error value earlier we were ignoring the return value of snd_ak4114_create and always returning 0. now we are returning the actual status. revo_init is calling this function, and revo_init is checking the return value. Signed-off-by: Sudip Mukherjee Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 1112ec1..1d81ae6 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -494,11 +494,13 @@ static int ap192_ak4114_init(struct snd_ice1712 *ice) ap192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &spec->ak4114); + if (err < 0) + return err; /* AK4114 in Revo cannot detect external rate correctly. * No reason to stop capture stream due to incorrect checks */ spec->ak4114->check_flags = AK4114_CHECK_NO_RATE; - return 0; /* error ignored; it's no fatal error */ + return 0; } static int revo_init(struct snd_ice1712 *ice) -- cgit v0.10.2 From 5195c14c8b27cc0b18220ddbf0e5ad3328a04187 Mon Sep 17 00:00:00 2001 From: bill bonaparte Date: Thu, 6 Nov 2014 14:36:48 +0100 Subject: netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse After removal of the central spinlock nf_conntrack_lock, in commit 93bb0ceb75be2 ("netfilter: conntrack: remove central spinlock nf_conntrack_lock"), it is possible to race against get_next_corpse(). The race is against the get_next_corpse() cleanup on the "unconfirmed" list (a per-cpu list with seperate locking), which set the DYING bit. Fix this race, in __nf_conntrack_confirm(), by removing the CT from unconfirmed list before checking the DYING bit. In case race occured, re-add the CT to the dying list. While at this, fix coding style of the comment that has been updated. Fixes: 93bb0ceb75be2 ("netfilter: conntrack: remove central spinlock nf_conntrack_lock") Reported-by: bill bonaparte Signed-off-by: bill bonaparte Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5016a69..2c69975 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -611,12 +611,16 @@ __nf_conntrack_confirm(struct sk_buff *skb) */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); pr_debug("Confirming conntrack %p\n", ct); - /* We have to check the DYING flag inside the lock to prevent - a race against nf_ct_get_next_corpse() possibly called from - user context, else we insert an already 'dead' hash, blocking - further use of that particular connection -JM */ + + /* We have to check the DYING flag after unlink to prevent + * a race against nf_ct_get_next_corpse() possibly called from + * user context, else we insert an already 'dead' hash, blocking + * further use of that particular connection -JM. + */ + nf_ct_del_from_dying_or_unconfirmed_list(ct); if (unlikely(nf_ct_is_dying(ct))) { + nf_ct_add_to_dying_list(ct); nf_conntrack_double_unlock(hash, reply_hash); local_bh_enable(); return NF_ACCEPT; @@ -636,8 +640,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) goto out; - nf_ct_del_from_dying_or_unconfirmed_list(ct); - /* Timer relative to confirmation time, not original setting time, otherwise we'd get timer wrap in weird delay cases. */ -- cgit v0.10.2 From 84bc88688e3f6ef843aa8803dbcd90168bb89faf Mon Sep 17 00:00:00 2001 From: Vincent BENAYOUN Date: Thu, 13 Nov 2014 13:47:26 +0100 Subject: inetdevice: fixed signed integer overflow There could be a signed overflow in the following code. The expression, (32-logmask) is comprised between 0 and 31 included. It may be equal to 31. In such a case the left shift will produce a signed integer overflow. According to the C99 Standard, this is an undefined behavior. A simple fix is to replace the signed int 1 with the unsigned int 1U. Signed-off-by: Vincent BENAYOUN Signed-off-by: David S. Miller diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 0068708..0a21fbe 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -242,7 +242,7 @@ static inline void in_dev_put(struct in_device *idev) static __inline__ __be32 inet_make_mask(int logmask) { if (logmask) - return htonl(~((1<<(32-logmask))-1)); + return htonl(~((1U<<(32-logmask))-1)); return 0; } -- cgit v0.10.2 From 23e62de33d179e229e4c1dfd93f90a3c7355c519 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 13 Nov 2014 16:38:12 -0800 Subject: net: Add vxlan_gso_check() helper Most NICs that report NETIF_F_GSO_UDP_TUNNEL support VXLAN, and not other UDP-based encapsulation protocols where the format and size of the header differs. This patch implements a generic ndo_gso_check() for VXLAN which will only advertise GSO support when the skb looks like it contains VXLAN (or no UDP tunnelling at all). Implementation shamelessly stolen from Tom Herbert: http://thread.gmane.org/gmane.linux.network/332428/focus=333111 Signed-off-by: Joe Stringer Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index fa9dc45..6b65863 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1571,6 +1571,19 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } +bool vxlan_gso_check(struct sk_buff *skb) +{ + if ((skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) && + (skb->inner_protocol_type != ENCAP_TYPE_ETHER || + skb->inner_protocol != htons(ETH_P_TEB) || + (skb_inner_mac_header(skb) - skb_transport_header(skb) != + sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(vxlan_gso_check); + #if IS_ENABLED(CONFIG_IPV6) static int vxlan6_xmit_skb(struct vxlan_sock *vs, struct dst_entry *dst, struct sk_buff *skb, diff --git a/include/net/vxlan.h b/include/net/vxlan.h index d5f59f3..afadf8e 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -45,6 +45,8 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be32 vni, bool xnet); +bool vxlan_gso_check(struct sk_buff *skb); + /* IP header + UDP + VXLAN + Ethernet header */ #define VXLAN_HEADROOM (20 + 8 + 8 + 14) /* IPv6 header + UDP + VXLAN + Ethernet header */ -- cgit v0.10.2 From 725d548f14362f10c9235ee4b32dc22e7c55e50c Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 13 Nov 2014 16:38:13 -0800 Subject: be2net: Implement ndo_gso_check() Use vxlan_gso_check() to advertise offload support for this NIC. Signed-off-by: Joe Stringer Acked-by: Sathya Perla Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 9a18e79..3e8475c 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4421,6 +4421,11 @@ static void be_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family, "Disabled VxLAN offloads for UDP port %d\n", be16_to_cpu(port)); } + +static bool be_gso_check(struct sk_buff *skb, struct net_device *dev) +{ + return vxlan_gso_check(skb); +} #endif static const struct net_device_ops be_netdev_ops = { @@ -4450,6 +4455,7 @@ static const struct net_device_ops be_netdev_ops = { #ifdef CONFIG_BE2NET_VXLAN .ndo_add_vxlan_port = be_add_vxlan_port, .ndo_del_vxlan_port = be_del_vxlan_port, + .ndo_gso_check = be_gso_check, #endif }; -- cgit v0.10.2 From 956bdab2e439881e151b7a1779e7f12a3778b68d Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 13 Nov 2014 16:38:14 -0800 Subject: net/mlx4_en: Implement ndo_gso_check() Use vxlan_gso_check() to advertise offload support for this NIC. Signed-off-by: Joe Stringer Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 02266e3..c5fcc56 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2355,6 +2355,11 @@ static void mlx4_en_del_vxlan_port(struct net_device *dev, queue_work(priv->mdev->workqueue, &priv->vxlan_del_task); } + +static bool mlx4_en_gso_check(struct sk_buff *skb, struct net_device *dev) +{ + return vxlan_gso_check(skb); +} #endif static const struct net_device_ops mlx4_netdev_ops = { @@ -2386,6 +2391,7 @@ static const struct net_device_ops mlx4_netdev_ops = { #ifdef CONFIG_MLX4_EN_VXLAN .ndo_add_vxlan_port = mlx4_en_add_vxlan_port, .ndo_del_vxlan_port = mlx4_en_del_vxlan_port, + .ndo_gso_check = mlx4_en_gso_check, #endif }; -- cgit v0.10.2 From 795a05c1c2a012ab2fd08f1523aa3c529049d291 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 13 Nov 2014 16:38:15 -0800 Subject: qlcnic: Implement ndo_gso_check() Use vxlan_gso_check() to advertise offload support for this NIC. Signed-off-by: Joe Stringer Acked-by: Shahed Shaikh Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index f5e29f7..a913b3a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -503,6 +503,11 @@ static void qlcnic_del_vxlan_port(struct net_device *netdev, adapter->flags |= QLCNIC_DEL_VXLAN_PORT; } + +static bool qlcnic_gso_check(struct sk_buff *skb, struct net_device *dev) +{ + return vxlan_gso_check(skb); +} #endif static const struct net_device_ops qlcnic_netdev_ops = { @@ -526,6 +531,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { #ifdef CONFIG_QLCNIC_VXLAN .ndo_add_vxlan_port = qlcnic_add_vxlan_port, .ndo_del_vxlan_port = qlcnic_del_vxlan_port, + .ndo_gso_check = qlcnic_gso_check, #endif #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = qlcnic_poll_controller, -- cgit v0.10.2 From ab64f16ff2e83371927c57a0380fd3c0fee5c1c1 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 11 Nov 2014 13:40:49 -0800 Subject: openvswitch: Fix memory leak. Need to free memory in case of sample action error. Introduced by commit 651887b0c22cffcfce7eb9c ("openvswitch: Sample action without side effects"). Signed-off-by: Pravin B Shelar diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 006886d..00e447a 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -722,8 +722,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_SAMPLE: err = sample(dp, skb, key, a); - if (unlikely(err)) /* skb already freed. */ - return err; break; } -- cgit v0.10.2 From 856447d0209c2214d806b30bd3b0d873db5998bd Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 11 Nov 2014 14:32:20 -0800 Subject: openvswitch: Fix checksum calculation when modifying ICMPv6 packets. The checksum of ICMPv6 packets uses the IP pseudoheader as part of the calculation, unlike ICMP in IPv4. This was not implemented, which means that modifying the IP addresses of an ICMPv6 packet would cause the checksum to no longer be correct as the psuedoheader did not match. Introduced by commit 3fdbd1ce11e5 ("openvswitch: add ipv6 'set' action"). Reported-by: Neal Shrader Signed-off-by: Jesse Gross Signed-off-by: Pravin B Shelar diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 00e447a..8c4229b 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -246,11 +246,11 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, { int transport_len = skb->len - skb_transport_offset(skb); - if (l4_proto == IPPROTO_TCP) { + if (l4_proto == NEXTHDR_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, addr, new_addr, 1); - } else if (l4_proto == IPPROTO_UDP) { + } else if (l4_proto == NEXTHDR_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) { struct udphdr *uh = udp_hdr(skb); @@ -261,6 +261,10 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, uh->check = CSUM_MANGLED_0; } } + } else if (l4_proto == NEXTHDR_ICMP) { + if (likely(transport_len >= sizeof(struct icmp6hdr))) + inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum, + skb, addr, new_addr, 1); } } -- cgit v0.10.2 From 19e7a3df7261c9b7ebced8163c383712d5b6ac6b Mon Sep 17 00:00:00 2001 From: Daniele Di Proietto Date: Tue, 11 Nov 2014 14:51:22 -0800 Subject: openvswitch: Fix NDP flow mask validation match_validate() enforce that a mask matching on NDP attributes has also an exact match on ICMPv6 type. The ICMPv6 type, which is 8-bit wide, is stored in the 'tp.src' field of 'struct sw_flow_key', which is 16-bit wide. Therefore, an exact match on ICMPv6 type should only check the first 8 bits. This commit fixes a bug that prevented flows with an exact match on NDP field from being installed Introduced by commit 03f0d916aa03 ("openvswitch: Mega flow implementation"). Signed-off-by: Daniele Di Proietto Signed-off-by: Pravin B Shelar diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 939bcb3..dda040e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -145,7 +145,7 @@ static bool match_validate(const struct sw_flow_match *match, if (match->key->eth.type == htons(ETH_P_ARP) || match->key->eth.type == htons(ETH_P_RARP)) { key_expected |= 1 << OVS_KEY_ATTR_ARP; - if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + if (match->mask && (match->mask->key.tp.src == htons(0xff))) mask_allowed |= 1 << OVS_KEY_ATTR_ARP; } -- cgit v0.10.2 From 8ec609d8b561468691b60347ff594bd443ea58c0 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 11 Nov 2014 15:55:16 -0800 Subject: openvswitch: Convert dp rcu read operation to locked operations dp read operations depends on ovs_dp_cmd_fill_info(). This API needs to looup vport to find dp name, but vport lookup can fail. Therefore to keep vport reference alive we need to take ovs lock. Introduced by commit 6093ae9abac1 ("openvswitch: Minimize dp and vport critical sections"). Signed-off-by: Pravin B Shelar Acked-by: Andy Zhou diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index e6d7255..f9e556b 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1265,7 +1265,7 @@ static size_t ovs_dp_cmd_msg_size(void) return msgsize; } -/* Called with ovs_mutex or RCU read lock. */ +/* Called with ovs_mutex. */ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { @@ -1555,7 +1555,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) if (!reply) return -ENOMEM; - rcu_read_lock(); + ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); if (IS_ERR(dp)) { err = PTR_ERR(dp); @@ -1564,12 +1564,12 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); - rcu_read_unlock(); + ovs_unlock(); return genlmsg_reply(reply, info); err_unlock_free: - rcu_read_unlock(); + ovs_unlock(); kfree_skb(reply); return err; } @@ -1581,8 +1581,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) int skip = cb->args[0]; int i = 0; - rcu_read_lock(); - list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) { + ovs_lock(); + list_for_each_entry(dp, &ovs_net->dps, list_node) { if (i >= skip && ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -1590,7 +1590,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) break; i++; } - rcu_read_unlock(); + ovs_unlock(); cb->args[0] = i; -- cgit v0.10.2 From fecaef85f7188ad1822210e2c7a7625c9a32a8e4 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Tue, 11 Nov 2014 14:36:30 -0800 Subject: openvswitch: Validate IPv6 flow key and mask values. Reject flow label key and mask values with invalid bits set. Introduced by commit 3fdbd1ce11e5 ("openvswitch: add ipv6 'set' action"). Signed-off-by: Jarno Rajahalme Acked-by: Jesse Gross Signed-off-by: Pravin B Shelar diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index dda040e..fa4ec2e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -689,6 +689,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; } + + if (ipv6_key->ipv6_label & htonl(0xFFF00000)) { + OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n", + ntohl(ipv6_key->ipv6_label), (1 << 20) - 1); + return -EINVAL; + } + SW_FLOW_KEY_PUT(match, ipv6.label, ipv6_key->ipv6_label, is_mask); SW_FLOW_KEY_PUT(match, ip.proto, -- cgit v0.10.2 From ccf54555da9a5e91e454b909ca6a5303c7d6b910 Mon Sep 17 00:00:00 2001 From: Cristina Ciocan Date: Tue, 11 Nov 2014 16:07:42 +0200 Subject: iio: Fix IIO_EVENT_CODE_EXTRACT_DIR bit mask The direction field is set on 7 bits, thus we need to AND it with 0111 111 mask in order to retrieve it, that is 0x7F, not 0xCF as it is now. Fixes: ade7ef7ba (staging:iio: Differential channel handling) Signed-off-by: Cristina Ciocan Cc: Signed-off-by: Jonathan Cameron diff --git a/include/linux/iio/events.h b/include/linux/iio/events.h index 8bbd7bc..03fa332 100644 --- a/include/linux/iio/events.h +++ b/include/linux/iio/events.h @@ -72,7 +72,7 @@ struct iio_event_data { #define IIO_EVENT_CODE_EXTRACT_TYPE(mask) ((mask >> 56) & 0xFF) -#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0xCF) +#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0x7F) #define IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask) ((mask >> 32) & 0xFF) -- cgit v0.10.2 From e90dea6aafa14eb60ab0927f6fe54c620f61ef74 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 10 Nov 2014 10:20:16 +0200 Subject: iio: accel: kxcjk-1013: Fix kxcjk10013_set_range Currently, we get the new GSEL bits by OR-ing the old values with the new ones. This only works first time when the old values are 0. Startup: * GSEL0 = 0, GSEL1 = 0 Set range to 4G: (GSEL0 = 1, GSEL1 = 0) * GSEL0 = 0 | 1 = 1 * GSEL1 = 0 | 0 = 0 * correct Change range to 2G: (GSEL0 = 0, GSEL1 = 0) * GSEL0 = 1 | 0 = 1 * GSEL1 = 0 | 0 = 0 * wrong, GSEL0 should be 0 This has the nice effect that we can use the full scale range, exported in in_accel_scale_available. Fixes: a735e3d7f03 (iio: accel: kxcjk-1013: Set adjustable range) Signed-off-by: Daniel Baluta Reviewed-by: Srinivas Pandruvada Cc: Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index a23e58c..320aa72 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -269,6 +269,8 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) return ret; } + ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 | + KXCJK1013_REG_CTRL1_BIT_GSEL1); ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); -- cgit v0.10.2 From fbbba1f89eb68e7d07707e104193d56de8e37fe5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 10 Nov 2014 16:04:06 +0800 Subject: iio: adc: men_z188_adc: Add terminating entry for men_z188_ids The mcb_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Cc: Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index b58d630..d095efe 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev) static const struct mcb_device_id men_z188_ids[] = { { .device = 0xbc }, + { } }; MODULE_DEVICE_TABLE(mcb, men_z188_ids); -- cgit v0.10.2 From 226424eee809251ec23bd4b09d8efba09c10fc3c Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 5 Nov 2014 16:11:44 +0000 Subject: perf: Fix corruption of sibling list with hotplug When a CPU hotplugged out, we call perf_remove_from_context() (via perf_event_exit_cpu()) to rip each CPU-bound event out of its PMU's cpu context, but leave siblings grouped together. Freeing of these events is left to the mercy of the usual refcounting. When a CPU-bound event's refcount drops to zero we cross-call to __perf_remove_from_context() to clean it up, detaching grouped siblings. This works when the relevant CPU is online, but will fail if the CPU is currently offline, and we won't detach the event from its siblings before freeing the event, leaving the sibling list corrupt. If the sibling list is later walked (e.g. because the CPU cam online again before a remaining sibling's refcount drops to zero), we will walk the now corrupted siblings list, potentially dereferencing garbage values. Given that the events should never be scheduled again (as we removed them from their context), we can simply detatch siblings when the CPU goes down in the first place. If the CPU comes back online, the redundant call to __perf_remove_from_context() is safe. Reported-by: Drew Richardson Signed-off-by: Mark Rutland Signed-off-by: Peter Zijlstra (Intel) Cc: vincent.weaver@maine.edu Cc: Vince Weaver Cc: Will Deacon Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Link: http://lkml.kernel.org/r/1415203904-25308-2-git-send-email-mark.rutland@arm.com Signed-off-by: Ingo Molnar diff --git a/kernel/events/core.c b/kernel/events/core.c index 2b02c9f..1cd5eef 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1562,8 +1562,10 @@ static void perf_remove_from_context(struct perf_event *event, bool detach_group if (!task) { /* - * Per cpu events are removed via an smp call and - * the removal is always successful. + * Per cpu events are removed via an smp call. The removal can + * fail if the CPU is currently offline, but in that case we + * already called __perf_remove_from_context from + * perf_event_exit_cpu. */ cpu_function_call(event->cpu, __perf_remove_from_context, &re); return; @@ -8117,7 +8119,7 @@ static void perf_pmu_rotate_stop(struct pmu *pmu) static void __perf_event_exit_context(void *__info) { - struct remove_event re = { .detach_group = false }; + struct remove_event re = { .detach_group = true }; struct perf_event_context *ctx = __info; perf_pmu_rotate_stop(ctx->pmu); -- cgit v0.10.2 From 41a134a5830a5e1396723ace0a63000780d6e267 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 3 Nov 2014 17:00:27 -0800 Subject: perf/x86/intel/uncore: Fix IRP uncore register offsets on Haswell EP The counter register offsets for the IRP box PMU for Haswell-EP were incorrect. The offsets actually changed over IvyBridge EP. Fix them to the correct values. For this we need to fork the read function from the IVB and use an own counter array. Tested-by: patrick.lu@intel.com Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/1415062828-19759-3-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index adf138e..0af1c93 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -2025,13 +2025,27 @@ static struct intel_uncore_type hswep_uncore_imc = { SNBEP_UNCORE_PCI_COMMON_INIT(), }; +static unsigned hswep_uncore_irp_ctrs[] = {0xa0, 0xa8, 0xb0, 0xb8}; + +static u64 hswep_uncore_irp_read_counter(struct intel_uncore_box *box, struct perf_event *event) +{ + struct pci_dev *pdev = box->pci_dev; + struct hw_perf_event *hwc = &event->hw; + u64 count = 0; + + pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx], (u32 *)&count); + pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx] + 4, (u32 *)&count + 1); + + return count; +} + static struct intel_uncore_ops hswep_uncore_irp_ops = { .init_box = snbep_uncore_pci_init_box, .disable_box = snbep_uncore_pci_disable_box, .enable_box = snbep_uncore_pci_enable_box, .disable_event = ivbep_uncore_irp_disable_event, .enable_event = ivbep_uncore_irp_enable_event, - .read_counter = ivbep_uncore_irp_read_counter, + .read_counter = hswep_uncore_irp_read_counter, }; static struct intel_uncore_type hswep_uncore_irp = { -- cgit v0.10.2 From 68055915c1c22489f9658bd2b7391bb11b2cf4e4 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 3 Nov 2014 17:00:28 -0800 Subject: perf/x86/intel/uncore: Fix boot crash on SBOX PMU on Haswell-EP There were several reports that on some systems writing the SBOX0 PMU initialization MSR would #GP at boot. This did not happen on all systems -- my two test systems booted fine. Writing the three initialization bits bit-by-bit seems to avoid the problem. So add a special callback to do just that. This replaces an earlier patch that disabled the SBOX. Reported-by: Alexei Starovoitov Reported-and-Tested-by: Patrick Lu Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/1415062828-19759-4-git-send-email-andi@firstfloor.org [ Fixed a whitespace error and added attribution tags that were left out inexplicably. ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index 0af1c93..f9ed429 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -486,14 +486,17 @@ static struct attribute_group snbep_uncore_qpi_format_group = { .attrs = snbep_uncore_qpi_formats_attr, }; -#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \ - .init_box = snbep_uncore_msr_init_box, \ +#define __SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \ .disable_box = snbep_uncore_msr_disable_box, \ .enable_box = snbep_uncore_msr_enable_box, \ .disable_event = snbep_uncore_msr_disable_event, \ .enable_event = snbep_uncore_msr_enable_event, \ .read_counter = uncore_msr_read_counter +#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \ + __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), \ + .init_box = snbep_uncore_msr_init_box \ + static struct intel_uncore_ops snbep_uncore_msr_ops = { SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), }; @@ -1919,6 +1922,30 @@ static struct intel_uncore_type hswep_uncore_cbox = { .format_group = &hswep_uncore_cbox_format_group, }; +/* + * Write SBOX Initialization register bit by bit to avoid spurious #GPs + */ +static void hswep_uncore_sbox_msr_init_box(struct intel_uncore_box *box) +{ + unsigned msr = uncore_msr_box_ctl(box); + + if (msr) { + u64 init = SNBEP_PMON_BOX_CTL_INT; + u64 flags = 0; + int i; + + for_each_set_bit(i, (unsigned long *)&init, 64) { + flags |= (1ULL << i); + wrmsrl(msr, flags); + } + } +} + +static struct intel_uncore_ops hswep_uncore_sbox_msr_ops = { + __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), + .init_box = hswep_uncore_sbox_msr_init_box +}; + static struct attribute *hswep_uncore_sbox_formats_attr[] = { &format_attr_event.attr, &format_attr_umask.attr, @@ -1944,7 +1971,7 @@ static struct intel_uncore_type hswep_uncore_sbox = { .event_mask = HSWEP_S_MSR_PMON_RAW_EVENT_MASK, .box_ctl = HSWEP_S0_MSR_PMON_BOX_CTL, .msr_offset = HSWEP_SBOX_MSR_OFFSET, - .ops = &snbep_uncore_msr_ops, + .ops = &hswep_uncore_sbox_msr_ops, .format_group = &hswep_uncore_sbox_format_group, }; -- cgit v0.10.2 From 00b4d9a14125f1e51874def2b9de6092e007412d Mon Sep 17 00:00:00 2001 From: Maxime COQUELIN Date: Thu, 6 Nov 2014 10:54:19 +0100 Subject: bitops: Fix shift overflow in GENMASK macros On some 32 bits architectures, including x86, GENMASK(31, 0) returns 0 instead of the expected ~0UL. This is the same on some 64 bits architectures with GENMASK_ULL(63, 0). This is due to an overflow in the shift operand, 1 << 32 for GENMASK, 1 << 64 for GENMASK_ULL. Reported-by: Eric Paire Suggested-by: Rasmus Villemoes Signed-off-by: Maxime Coquelin Signed-off-by: Peter Zijlstra (Intel) Cc: # v3.13+ Cc: linux@rasmusvillemoes.dk Cc: gong.chen@linux.intel.com Cc: John Sullivan Cc: Linus Torvalds Cc: Paul E. McKenney Cc: Theodore Ts'o Fixes: 10ef6b0dffe4 ("bitops: Introduce a more generic BITMASK macro") Link: http://lkml.kernel.org/r/1415267659-10563-1-git-send-email-maxime.coquelin@st.com Signed-off-by: Ingo Molnar diff --git a/include/linux/bitops.h b/include/linux/bitops.h index be5fd38..5d858e0 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -18,8 +18,11 @@ * position @h. For example * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ -#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l)) -#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l)) +#define GENMASK(h, l) \ + (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define GENMASK_ULL(h, l) \ + (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) extern unsigned int __sw_hweight8(unsigned int w); extern unsigned int __sw_hweight16(unsigned int w); -- cgit v0.10.2 From 7af683350cb0ddd0e9d3819b4eb7abe9e2d3e709 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 10 Nov 2014 10:54:35 +0100 Subject: sched/numa: Avoid selecting oneself as swap target Because the whole numa task selection stuff runs with preemption enabled (its long and expensive) we can end up migrating and selecting oneself as a swap target. This doesn't really work out well -- we end up trying to acquire the same lock twice for the swap migrate -- so avoid this. Reported-and-Tested-by: Sasha Levin Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20141110100328.GF29390@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 34baa60..3af3d1e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1180,6 +1180,13 @@ static void task_numa_compare(struct task_numa_env *env, raw_spin_unlock_irq(&dst_rq->lock); /* + * Because we have preemption enabled we can get migrated around and + * end try selecting ourselves (current == env->p) as a swap candidate. + */ + if (cur == env->p) + goto unlock; + + /* * "imp" is the fault differential for the source task between the * source and destination node. Calculate the total differential for * the source task and potential destination task. The more negative -- cgit v0.10.2 From 23cfa361f3e54a3e184a5e126bbbdd95f984881a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 12 Nov 2014 12:37:37 +0100 Subject: sched/cputime: Fix cpu_timer_sample_group() double accounting While looking over the cpu-timer code I found that we appear to add the delta for the calling task twice, through: cpu_timer_sample_group() thread_group_cputimer() thread_group_cputime() times->sum_exec_runtime += task_sched_runtime(); *sample = cputime.sum_exec_runtime + task_delta_exec(); Which would make the sample run ahead, making the sleep short. Signed-off-by: Peter Zijlstra (Intel) Cc: KOSAKI Motohiro Cc: Oleg Nesterov Cc: Stanislaw Gruszka Cc: Christoph Lameter Cc: Frederic Weisbecker Cc: Linus Torvalds Cc: Rik van Riel Cc: Tejun Heo Link: http://lkml.kernel.org/r/20141112113737.GI10476@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index 8422b4e..b9376cd 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -77,11 +77,6 @@ static inline unsigned int kstat_cpu_irqs_sum(unsigned int cpu) return kstat_cpu(cpu).irqs_sum; } -/* - * Lock/unlock the current runqueue - to extract task statistics: - */ -extern unsigned long long task_delta_exec(struct task_struct *); - extern void account_user_time(struct task_struct *, cputime_t, cputime_t); extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t); extern void account_steal_time(cputime_t); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5f12ca6..797a6c8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2499,19 +2499,6 @@ static u64 do_task_delta_exec(struct task_struct *p, struct rq *rq) return ns; } -unsigned long long task_delta_exec(struct task_struct *p) -{ - unsigned long flags; - struct rq *rq; - u64 ns = 0; - - rq = task_rq_lock(p, &flags); - ns = do_task_delta_exec(p, rq); - task_rq_unlock(rq, p, &flags); - - return ns; -} - /* * Return accounted runtime for the task. * In case the task is currently running, return the runtime plus current's diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 492b986..a16b678 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -553,7 +553,7 @@ static int cpu_timer_sample_group(const clockid_t which_clock, *sample = cputime_to_expires(cputime.utime); break; case CPUCLOCK_SCHED: - *sample = cputime.sum_exec_runtime + task_delta_exec(p); + *sample = cputime.sum_exec_runtime; break; } return 0; -- cgit v0.10.2 From 6e998916dfe327e785e7c2447959b2c1a3ea4930 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Nov 2014 16:58:44 +0100 Subject: sched/cputime: Fix clock_nanosleep()/clock_gettime() inconsistency Commit d670ec13178d0 "posix-cpu-timers: Cure SMP wobbles" fixes one glibc test case in cost of breaking another one. After that commit, calling clock_nanosleep(TIMER_ABSTIME, X) and then clock_gettime(&Y) can result of Y time being smaller than X time. Reproducer/tester can be found further below, it can be compiled and ran by: gcc -o tst-cpuclock2 tst-cpuclock2.c -pthread while ./tst-cpuclock2 ; do : ; done This reproducer, when running on a buggy kernel, will complain about "clock_gettime difference too small". Issue happens because on start in thread_group_cputimer() we initialize sum_exec_runtime of cputimer with threads runtime not yet accounted and then add the threads runtime to running cputimer again on scheduler tick, making it's sum_exec_runtime bigger than actual threads runtime. KOSAKI Motohiro posted a fix for this problem, but that patch was never applied: https://lkml.org/lkml/2013/5/26/191 . This patch takes different approach to cure the problem. It calls update_curr() when cputimer starts, that assure we will have updated stats of running threads and on the next schedule tick we will account only the runtime that elapsed from cputimer start. That also assure we have consistent state between cpu times of individual threads and cpu time of the process consisted by those threads. Full reproducer (tst-cpuclock2.c): #define _GNU_SOURCE #include #include #include #include #include #include #include /* Parameters for the Linux kernel ABI for CPU clocks. */ #define CPUCLOCK_SCHED 2 #define MAKE_PROCESS_CPUCLOCK(pid, clock) \ ((~(clockid_t) (pid) << 3) | (clockid_t) (clock)) static pthread_barrier_t barrier; /* Help advance the clock. */ static void *chew_cpu(void *arg) { pthread_barrier_wait(&barrier); while (1) ; return NULL; } /* Don't use the glibc wrapper. */ static int do_nanosleep(int flags, const struct timespec *req) { clockid_t clock_id = MAKE_PROCESS_CPUCLOCK(0, CPUCLOCK_SCHED); return syscall(SYS_clock_nanosleep, clock_id, flags, req, NULL); } static int64_t tsdiff(const struct timespec *before, const struct timespec *after) { int64_t before_i = before->tv_sec * 1000000000ULL + before->tv_nsec; int64_t after_i = after->tv_sec * 1000000000ULL + after->tv_nsec; return after_i - before_i; } int main(void) { int result = 0; pthread_t th; pthread_barrier_init(&barrier, NULL, 2); if (pthread_create(&th, NULL, chew_cpu, NULL) != 0) { perror("pthread_create"); return 1; } pthread_barrier_wait(&barrier); /* The test. */ struct timespec before, after, sleeptimeabs; int64_t sleepdiff, diffabs; const struct timespec sleeptime = {.tv_sec = 0,.tv_nsec = 100000000 }; /* The relative nanosleep. Not sure why this is needed, but its presence seems to make it easier to reproduce the problem. */ if (do_nanosleep(0, &sleeptime) != 0) { perror("clock_nanosleep"); return 1; } /* Get the current time. */ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &before) < 0) { perror("clock_gettime[2]"); return 1; } /* Compute the absolute sleep time based on the current time. */ uint64_t nsec = before.tv_nsec + sleeptime.tv_nsec; sleeptimeabs.tv_sec = before.tv_sec + nsec / 1000000000; sleeptimeabs.tv_nsec = nsec % 1000000000; /* Sleep for the computed time. */ if (do_nanosleep(TIMER_ABSTIME, &sleeptimeabs) != 0) { perror("absolute clock_nanosleep"); return 1; } /* Get the time after the sleep. */ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &after) < 0) { perror("clock_gettime[3]"); return 1; } /* The time after sleep should always be equal to or after the absolute sleep time passed to clock_nanosleep. */ sleepdiff = tsdiff(&sleeptimeabs, &after); if (sleepdiff < 0) { printf("absolute clock_nanosleep woke too early: %" PRId64 "\n", sleepdiff); result = 1; printf("Before %llu.%09llu\n", before.tv_sec, before.tv_nsec); printf("After %llu.%09llu\n", after.tv_sec, after.tv_nsec); printf("Sleep %llu.%09llu\n", sleeptimeabs.tv_sec, sleeptimeabs.tv_nsec); } /* The difference between the timestamps taken before and after the clock_nanosleep call should be equal to or more than the duration of the sleep. */ diffabs = tsdiff(&before, &after); if (diffabs < sleeptime.tv_nsec) { printf("clock_gettime difference too small: %" PRId64 "\n", diffabs); result = 1; } pthread_cancel(th); return result; } Signed-off-by: Stanislaw Gruszka Signed-off-by: Peter Zijlstra (Intel) Cc: Rik van Riel Cc: Frederic Weisbecker Cc: KOSAKI Motohiro Cc: Oleg Nesterov Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20141112155843.GA24803@redhat.com Signed-off-by: Ingo Molnar diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 797a6c8..24beb9b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2475,31 +2475,6 @@ EXPORT_PER_CPU_SYMBOL(kstat); EXPORT_PER_CPU_SYMBOL(kernel_cpustat); /* - * Return any ns on the sched_clock that have not yet been accounted in - * @p in case that task is currently running. - * - * Called with task_rq_lock() held on @rq. - */ -static u64 do_task_delta_exec(struct task_struct *p, struct rq *rq) -{ - u64 ns = 0; - - /* - * Must be ->curr _and_ ->on_rq. If dequeued, we would - * project cycles that may never be accounted to this - * thread, breaking clock_gettime(). - */ - if (task_current(rq, p) && task_on_rq_queued(p)) { - update_rq_clock(rq); - ns = rq_clock_task(rq) - p->se.exec_start; - if ((s64)ns < 0) - ns = 0; - } - - return ns; -} - -/* * Return accounted runtime for the task. * In case the task is currently running, return the runtime plus current's * pending runtime that have not been accounted yet. @@ -2508,7 +2483,7 @@ unsigned long long task_sched_runtime(struct task_struct *p) { unsigned long flags; struct rq *rq; - u64 ns = 0; + u64 ns; #if defined(CONFIG_64BIT) && defined(CONFIG_SMP) /* @@ -2527,7 +2502,16 @@ unsigned long long task_sched_runtime(struct task_struct *p) #endif rq = task_rq_lock(p, &flags); - ns = p->se.sum_exec_runtime + do_task_delta_exec(p, rq); + /* + * Must be ->curr _and_ ->on_rq. If dequeued, we would + * project cycles that may never be accounted to this + * thread, breaking clock_gettime(). + */ + if (task_current(rq, p) && task_on_rq_queued(p)) { + update_rq_clock(rq); + p->sched_class->update_curr(rq); + } + ns = p->se.sum_exec_runtime; task_rq_unlock(rq, p, &flags); return ns; diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 5285332..28fa9d9 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1701,4 +1701,6 @@ const struct sched_class dl_sched_class = { .prio_changed = prio_changed_dl, .switched_from = switched_from_dl, .switched_to = switched_to_dl, + + .update_curr = update_curr_dl, }; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3af3d1e..ef2b104 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -726,6 +726,11 @@ static void update_curr(struct cfs_rq *cfs_rq) account_cfs_rq_runtime(cfs_rq, delta_exec); } +static void update_curr_fair(struct rq *rq) +{ + update_curr(cfs_rq_of(&rq->curr->se)); +} + static inline void update_stats_wait_start(struct cfs_rq *cfs_rq, struct sched_entity *se) { @@ -7956,6 +7961,8 @@ const struct sched_class fair_sched_class = { .get_rr_interval = get_rr_interval_fair, + .update_curr = update_curr_fair, + #ifdef CONFIG_FAIR_GROUP_SCHED .task_move_group = task_move_group_fair, #endif diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index d024e6c..20bca39 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2128,6 +2128,8 @@ const struct sched_class rt_sched_class = { .prio_changed = prio_changed_rt, .switched_to = switched_to_rt, + + .update_curr = update_curr_rt, }; #ifdef CONFIG_SCHED_DEBUG diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 24156c84..2df8ef0 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1135,6 +1135,8 @@ struct sched_class { unsigned int (*get_rr_interval) (struct rq *rq, struct task_struct *task); + void (*update_curr) (struct rq *rq); + #ifdef CONFIG_FAIR_GROUP_SCHED void (*task_move_group) (struct task_struct *p, int on_rq); #endif -- cgit v0.10.2 From 2cd3949f702692cf4c5d05b463f19cd706a92dd3 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 11 Nov 2014 14:01:33 -0800 Subject: x86: Require exact match for 'noxsave' command line option We have some very similarly named command-line options: arch/x86/kernel/cpu/common.c:__setup("noxsave", x86_xsave_setup); arch/x86/kernel/cpu/common.c:__setup("noxsaveopt", x86_xsaveopt_setup); arch/x86/kernel/cpu/common.c:__setup("noxsaves", x86_xsaves_setup); __setup() is designed to match options that take arguments, like "foo=bar" where you would have: __setup("foo", x86_foo_func...); The problem is that "noxsave" actually _matches_ "noxsaves" in the same way that "foo" matches "foo=bar". If you boot an old kernel that does not know about "noxsaves" with "noxsaves" on the command line, it will interpret the argument as "noxsave", which is not what you want at all. This makes the "noxsave" handler only return success when it finds an *exact* match. [ tglx: We really need to make __setup() more robust. ] Signed-off-by: Dave Hansen Cc: Dave Hansen Cc: Fenghua Yu Cc: x86@kernel.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20141111220133.FE053984@viggo.jf.intel.com Signed-off-by: Thomas Gleixner diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 4b4f78c..cfa9b5b 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -146,6 +146,8 @@ EXPORT_PER_CPU_SYMBOL_GPL(gdt_page); static int __init x86_xsave_setup(char *s) { + if (strlen(s)) + return 0; setup_clear_cpu_cap(X86_FEATURE_XSAVE); setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT); setup_clear_cpu_cap(X86_FEATURE_XSAVES); -- cgit v0.10.2 From 49dd18ba4615eaa72f15c9087dea1c2ab4744cf5 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Fri, 14 Nov 2014 13:14:32 +0200 Subject: ipv4: Fix incorrect error code when adding an unreachable route Trying to add an unreachable route incorrectly returns -ESRCH if if custom FIB rules are present: [root@localhost ~]# ip route add 74.125.31.199 dev eth0 via 1.2.3.4 RTNETLINK answers: Network is unreachable [root@localhost ~]# ip rule add to 55.66.77.88 table 200 [root@localhost ~]# ip route add 74.125.31.199 dev eth0 via 1.2.3.4 RTNETLINK answers: No such process [root@localhost ~]# Commit 83886b6b636173b206f475929e58fac75c6f2446 ("[NET]: Change "not found" return value for rule lookup") changed fib_rules_lookup() to use -ESRCH as a "not found" code internally, but for user space it should be translated into -ENETUNREACH. Handle the translation centrally in ipv4-specific fib_lookup(), leaving the DECnet case alone. On a related note, commit b7a71b51ee37d919e4098cd961d59a883fd272d8 ("ipv4: removed redundant conditional") removed a similar translation from ip_route_input_slow() prematurely AIUI. Fixes: b7a71b51ee37 ("ipv4: removed redundant conditional") Signed-off-by: Panu Matilainen Signed-off-by: David S. Miller diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index f2e1573..8f7bd56 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -62,6 +62,10 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) else res->tclassid = 0; #endif + + if (err == -ESRCH) + err = -ENETUNREACH; + return err; } EXPORT_SYMBOL_GPL(__fib_lookup); -- cgit v0.10.2 From 9f458945080f9e618641ff3ef04e60be0895d7e4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Fri, 14 Nov 2014 15:16:47 +0100 Subject: reciprocal_div: objects with exported symbols should be obj-y rather than lib-y Otherwise the exported symbols might be discarded because of no users in vmlinux. Reported-by: Jim Davis Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller diff --git a/lib/Makefile b/lib/Makefile index 7512dc9..0211d2b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -10,7 +10,7 @@ endif lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o timerqueue.o\ idr.o int_sqrt.o extable.o \ - sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ + sha1.o md5.o irq_regs.o argv_split.o \ proportions.o flex_proportions.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ earlycpio.o @@ -26,7 +26,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \ bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o percpu_ida.o hash.o rhashtable.o + percpu-refcount.o percpu_ida.o hash.o rhashtable.o reciprocal_div.o obj-y += string_helpers.o obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += kstrtox.o -- cgit v0.10.2 From 35717d8d6fc6fc50692273d6667a0a575c26aa93 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Fri, 14 Nov 2014 15:42:52 +0100 Subject: drivers: net: cpsw: Fix TX_IN_SEL offset The TX_IN_SEL offset for the CPSW_PORT/TX_IN_CTL register was incorrect. This caused the Dual MAC mode to never get set when it should. It also caused possible unintentional setting of a bit in the CPSW_PORT/TX_BLKS_REM register. The purpose of setting the Dual MAC mode for this register is to: "... allow packets from both ethernet ports to be written into the FIFO without one port starving the other port." - AM335x ARM TRM Signed-off-by: John Ogness Reviewed-by: Mugunthan V N Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index d879448..c560f9a 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -129,9 +129,9 @@ do { \ #define CPSW_VLAN_AWARE BIT(1) #define CPSW_ALE_VLAN_AWARE 1 -#define CPSW_FIFO_NORMAL_MODE (0 << 15) -#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15) -#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15) +#define CPSW_FIFO_NORMAL_MODE (0 << 16) +#define CPSW_FIFO_DUAL_MAC_MODE (1 << 16) +#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 16) #define CPSW_INTPACEEN (0x3f << 16) #define CPSW_INTPRESCALE_MASK (0x7FF << 0) -- cgit v0.10.2 From 8c2dd54485ccee7fc4086611e188478584758c8d Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 15 Nov 2014 02:11:59 +0300 Subject: ieee802154: fix error handling in ieee802154fake_probe() In case of any failure ieee802154fake_probe() just calls unregister_netdev(). But it does not look safe to unregister netdevice before it was registered. The patch implements straightforward resource deallocation in case of failure in ieee802154fake_probe(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: David S. Miller diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c index 9ce854f..6cbc56a 100644 --- a/drivers/net/ieee802154/fakehard.c +++ b/drivers/net/ieee802154/fakehard.c @@ -377,17 +377,20 @@ static int ieee802154fake_probe(struct platform_device *pdev) err = wpan_phy_register(phy); if (err) - goto out; + goto err_phy_reg; err = register_netdev(dev); - if (err < 0) - goto out; + if (err) + goto err_netdev_reg; dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n"); return 0; -out: - unregister_netdev(dev); +err_netdev_reg: + wpan_phy_unregister(phy); +err_phy_reg: + free_netdev(dev); + wpan_phy_free(phy); return err; } -- cgit v0.10.2 From 52cff74eef5dd7bdab759300e7d1ca36eba18254 Mon Sep 17 00:00:00 2001 From: Anish Bhatt Date: Fri, 14 Nov 2014 16:38:31 -0800 Subject: dcbnl : Disable software interrupts before taking dcb_lock Solves possible lockup issues that can be seen from firmware DCB agents calling into the DCB app api. DCB firmware event queues can be tied in with NAPI so that dcb events are generated in softIRQ context. This can results in calls to dcb_*app() functions which try to take the dcb_lock. If the the event triggers while we also have the dcb_lock because lldpad or some other agent happened to be issuing a get/set command we could see a cpu lockup. This code was not originally written with firmware agents in mind, hence grabbing dcb_lock from softIRQ context was not considered. Signed-off-by: Anish Bhatt Signed-off-by: David S. Miller diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index ca11d28..93ea801 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1080,13 +1080,13 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) if (!app) return -EMSGSIZE; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); list_for_each_entry(itr, &dcb_app_list, list) { if (itr->ifindex == netdev->ifindex) { err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app), &itr->app); if (err) { - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return -EMSGSIZE; } } @@ -1097,7 +1097,7 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) else dcbx = -EOPNOTSUPP; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); nla_nest_end(skb, app); /* get peer info if available */ @@ -1234,7 +1234,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) } /* local app */ - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE); if (!app) goto dcb_unlock; @@ -1271,7 +1271,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) else dcbx = -EOPNOTSUPP; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); /* features flags */ if (ops->getfeatcfg) { @@ -1326,7 +1326,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) return 0; dcb_unlock: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); nla_put_failure: return err; } @@ -1762,10 +1762,10 @@ u8 dcb_getapp(struct net_device *dev, struct dcb_app *app) struct dcb_app_type *itr; u8 prio = 0; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) prio = itr->app.priority; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return prio; } @@ -1789,7 +1789,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and replace */ if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) { if (new->priority) @@ -1804,7 +1804,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new) if (new->priority) err = dcb_app_add(new, dev->ifindex); out: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1823,10 +1823,10 @@ u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app) struct dcb_app_type *itr; u8 prio = 0; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) prio |= 1 << itr->app.priority; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return prio; } @@ -1850,7 +1850,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and abort if found */ if (dcb_app_lookup(new, dev->ifindex, new->priority)) { err = -EEXIST; @@ -1859,7 +1859,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) err = dcb_app_add(new, dev->ifindex); out: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1882,7 +1882,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and remove it. */ if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) { list_del(&itr->list); @@ -1890,7 +1890,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) err = 0; } - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1902,12 +1902,12 @@ static void dcb_flushapp(void) struct dcb_app_type *app; struct dcb_app_type *tmp; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); list_for_each_entry_safe(app, tmp, &dcb_app_list, list) { list_del(&app->list); kfree(app); } - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); } static int __init dcbnl_init(void) -- cgit v0.10.2 From 10bef28973071266e85fe7ea50e08ce8f46dc1ca Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:33:25 -0700 Subject: iio: gyro: bmg160: Error handling when mode set fails When mode set fails due to some transient failures, it will atleast reset the state of runtime usage count and also let the runtime suspend retry from the driver framework. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 1f967e0d..22bf9db 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -250,6 +250,9 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmg160_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } #endif @@ -705,6 +708,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev, ret = bmg160_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -871,6 +875,7 @@ static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmg160_setup_new_data_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1169,8 +1174,15 @@ static int bmg160_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmg160_data *data = iio_priv(indio_dev); + int ret; - return bmg160_set_mode(data, BMG160_MODE_SUSPEND); + ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); + if (ret < 0) { + dev_err(&data->client->dev, "set mode failed\n"); + return -EAGAIN; + } + + return 0; } static int bmg160_runtime_resume(struct device *dev) -- cgit v0.10.2 From 5af6b30788d4f745e62e5cb53850f80222571d3a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:33:26 -0700 Subject: iio: gyro: bmg160: Don't let interrupt mode to be open drain Change the mode to push/pull type instead of open drain as some platforms fails to drive the GPIO pin with open drain. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 22bf9db..fa2e376 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -67,6 +67,9 @@ #define BMG160_REG_INT_EN_0 0x15 #define BMG160_DATA_ENABLE_INT BIT(7) +#define BMG160_REG_INT_EN_1 0x16 +#define BMG160_INT1_BIT_OD BIT(1) + #define BMG160_REG_XOUT_L 0x02 #define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) @@ -222,6 +225,19 @@ static int bmg160_chip_init(struct bmg160_data *data) data->slope_thres = ret; /* Set default interrupt mode */ + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_en_1\n"); + return ret; + } + ret &= ~BMG160_INT1_BIT_OD; + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_en_1\n"); + return ret; + } + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_INT_RST_LATCH, BMG160_INT_MODE_LATCH_INT | -- cgit v0.10.2 From cb80f6a36be9b3007f23c2d37eddc90a37dac87f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:33:27 -0700 Subject: iio: gyro: bmg160: Send x, y and z motion separately This chip is capable to identify motion across x, y and z axes. So send different events. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index fa2e376..9f751d6 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -85,6 +85,9 @@ #define BMG160_REG_INT_STATUS_2 0x0B #define BMG160_ANY_MOTION_MASK 0x07 +#define BMG160_ANY_MOTION_BIT_X BIT(0) +#define BMG160_ANY_MOTION_BIT_Y BIT(1) +#define BMG160_ANY_MOTION_BIT_Z BIT(2) #define BMG160_REG_TEMP 0x08 #define BMG160_TEMP_CENTER_VAL 23 @@ -929,10 +932,24 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) else dir = IIO_EV_DIR_FALLING; - if (ret & BMG160_ANY_MOTION_MASK) + if (ret & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Y) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Z, IIO_EV_TYPE_ROC, dir), data->timestamp); -- cgit v0.10.2 From 6896ab3a8cfe383cc0c98790b62245a84c6335f7 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:33:28 -0700 Subject: iio: gyro: bmg160: Fix iio_event_spec direction Change event spec direction from IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING to IIO_EV_DIR_EITHER Suggested-by: Daniel Baluta Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 9f751d6..d2fa526 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -766,7 +766,7 @@ static const struct attribute_group bmg160_attrs_group = { static const struct iio_event_spec bmg160_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) }; -- cgit v0.10.2 From aaeecd80440bc5e06de7d2b741cd403325d8ca7a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:35:31 -0700 Subject: iio: accel: bmc150: Error handling when mode set fails When mode set fails due to some transient failures, it will atleast reset the state of runtime usage count and also let the runtime suspend retry from the driver framework. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 22c096c..fdb101c 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -536,6 +536,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmc150_accel_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } @@ -811,6 +814,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev, ret = bmc150_accel_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1054,6 +1058,7 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmc150_accel_setup_new_data_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1354,10 +1359,14 @@ static int bmc150_accel_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; dev_dbg(&data->client->dev, __func__); + ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + if (ret < 0) + return -EAGAIN; - return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + return 0; } static int bmc150_accel_runtime_resume(struct device *dev) -- cgit v0.10.2 From 8d5a9781920171a36e5826248cd56b4b6a734335 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:35:32 -0700 Subject: iio: accel: bmc150: Send x, y and z motion separately This chip is capable to identify motion across x, y and z axes. So send different events. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index fdb101c..c6df8ee0 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -44,6 +44,9 @@ #define BMC150_ACCEL_REG_INT_STATUS_2 0x0B #define BMC150_ACCEL_ANY_MOTION_MASK 0x07 +#define BMC150_ACCEL_ANY_MOTION_BIT_X BIT(0) +#define BMC150_ACCEL_ANY_MOTION_BIT_Y BIT(1) +#define BMC150_ACCEL_ANY_MOTION_BIT_Z BIT(2) #define BMC150_ACCEL_ANY_MOTION_BIT_SIGN BIT(3) #define BMC150_ACCEL_REG_PMU_LPW 0x11 @@ -1097,12 +1100,26 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private) else dir = IIO_EV_DIR_RISING; - if (ret & BMC150_ACCEL_ANY_MOTION_MASK) + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_X, IIO_EV_TYPE_ROC, - IIO_EV_DIR_EITHER), + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), data->timestamp); ack_intr_status: if (!data->dready_trigger_on) -- cgit v0.10.2 From 1174124c45209796b1a2853a411cf47cbfcaf7da Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:35:34 -0700 Subject: iio: accel: bmc150: Fix iio_event_spec direction Change event spec direction from IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING to IIO_EV_DIR_EITHER Suggested-by: Daniel Baluta Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index c6df8ee0..352d959 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -853,7 +853,7 @@ static const struct attribute_group bmc150_accel_attrs_group = { static const struct iio_event_spec bmc150_accel_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) -- cgit v0.10.2 From 9e8e228f2bf066c37eeee7c25b810cad3235427a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Oct 2014 20:35:33 -0700 Subject: iio: accel: bmc150: set low default thresholds Set the threshold to low by default. With this thresholds any movement on the device with this sensor will generate event. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 352d959..513bd6d 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -95,9 +95,9 @@ #define BMC150_ACCEL_SLOPE_THRES_MASK 0xFF /* Slope duration in terms of number of samples */ -#define BMC150_ACCEL_DEF_SLOPE_DURATION 2 +#define BMC150_ACCEL_DEF_SLOPE_DURATION 1 /* in terms of multiples of g's/LSB, based on range */ -#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 5 +#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 1 #define BMC150_ACCEL_REG_XOUT_L 0x02 -- cgit v0.10.2 From bb2bdeb83fb125c95e47fc7eca2a3e8f868e2a74 Mon Sep 17 00:00:00 2001 From: Martin Hauke Date: Sun, 16 Nov 2014 19:55:25 +0100 Subject: qmi_wwan: Add support for HP lt4112 LTE/HSPA+ Gobi 4G Modem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added the USB VID/PID for the HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) Signed-off-by: Martin Hauke Acked-by: Bjørn Mork Signed-off-by: David S. Miller diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 22756db..b8a82b8 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -780,6 +780,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ + {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ -- cgit v0.10.2 From feb91a02ccb09661507f170b2a444aec94f307f9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 5 Nov 2014 20:27:38 +0100 Subject: ipv6: mld: fix add_grhead skb_over_panic for devs with large MTUs It has been reported that generating an MLD listener report on devices with large MTUs (e.g. 9000) and a high number of IPv6 addresses can trigger a skb_over_panic(): skbuff: skb_over_panic: text:ffffffff80612a5d len:3776 put:20 head:ffff88046d751000 data:ffff88046d751010 tail:0xed0 end:0xec0 dev:port1 ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:100! invalid opcode: 0000 [#1] SMP Modules linked in: ixgbe(O) CPU: 3 PID: 0 Comm: swapper/3 Tainted: G O 3.14.23+ #4 [...] Call Trace: [] ? skb_put+0x3a/0x3b [] ? add_grhead+0x45/0x8e [] ? add_grec+0x394/0x3d4 [] ? mld_ifc_timer_expire+0x195/0x20d [] ? mld_dad_timer_expire+0x45/0x45 [] ? call_timer_fn.isra.29+0x12/0x68 [] ? run_timer_softirq+0x163/0x182 [] ? __do_softirq+0xe0/0x21d [] ? irq_exit+0x4e/0xd3 [] ? smp_apic_timer_interrupt+0x3b/0x46 [] ? apic_timer_interrupt+0x6a/0x70 mld_newpack() skb allocations are usually requested with dev->mtu in size, since commit 72e09ad107e7 ("ipv6: avoid high order allocations") we have changed the limit in order to be less likely to fail. However, in MLD/IGMP code, we have some rather ugly AVAILABLE(skb) macros, which determine if we may end up doing an skb_put() for adding another record. To avoid possible fragmentation, we check the skb's tailroom as skb->dev->mtu - skb->len, which is a wrong assumption as the actual max allocation size can be much smaller. The IGMP case doesn't have this issue as commit 57e1ab6eaddc ("igmp: refine skb allocations") stores the allocation size in the cb[]. Set a reserved_tailroom to make it fit into the MTU and use skb_availroom() helper instead. This also allows to get rid of igmp_skb_size(). Reported-by: Wei Liu Fixes: 72e09ad107e7 ("ipv6: avoid high order allocations") Signed-off-by: Daniel Borkmann Cc: Eric Dumazet Cc: Hannes Frederic Sowa Cc: David L Stevens Acked-by: Eric Dumazet Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index fb70e3e..bb15d0e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -318,9 +318,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) return scount; } -#define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb)) - -static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) +static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) { struct sk_buff *skb; struct rtable *rt; @@ -330,6 +328,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) struct flowi4 fl4; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; + unsigned int size = mtu; while (1) { skb = alloc_skb(size + hlen + tlen, @@ -341,7 +340,6 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) return NULL; } skb->priority = TC_PRIO_CONTROL; - igmp_skb_size(skb) = size; rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 0, 0, @@ -354,6 +352,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) skb_dst_set(skb, &rt->dst); skb->dev = dev; + skb->reserved_tailroom = skb_end_offset(skb) - + min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); skb_reset_network_header(skb); @@ -423,8 +423,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, return skb; } -#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \ - skb_tailroom(skb)) : 0) +#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 9648de2..ed2c4e4 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1550,7 +1550,7 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, hdr->daddr = *daddr; } -static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) +static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu) { struct net_device *dev = idev->dev; struct net *net = dev_net(dev); @@ -1561,13 +1561,13 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) const struct in6_addr *saddr; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; + unsigned int size = mtu + hlen + tlen; int err; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; /* we assume size > sizeof(ra) here */ - size += hlen + tlen; /* limit our allocations to order-0 page */ size = min_t(int, size, SKB_MAX_ORDER(0, 0)); skb = sock_alloc_send_skb(sk, size, 1, &err); @@ -1576,6 +1576,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) return NULL; skb->priority = TC_PRIO_CONTROL; + skb->reserved_tailroom = skb_end_offset(skb) - + min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { @@ -1690,8 +1692,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, return skb; } -#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ - skb_tailroom(skb)) : 0) +#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted, int crsend) -- cgit v0.10.2 From bce4f9e764c36bc35dd5c9cf9e057c09f422397d Mon Sep 17 00:00:00 2001 From: Ben Sagal Date: Sun, 16 Nov 2014 17:23:40 -0800 Subject: Input: synaptics - adjust min/max on Thinkpad E540 The LEN2006 Synaptics touchpad (as found in Thinkpad E540) returns wrong min max values. touchpad-edge-detector output: > Touchpad SynPS/2 Synaptics TouchPad on /dev/input/event6 > Move one finger around the touchpad to detect the actual edges > Kernel says: x [1472..5674], y [1408..4684] > Touchpad sends: x [1264..5675], y [1171..4688] Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88211 Cc: stable@vger.kernel.org Signed-off-by: Binyamin Sagal Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2a7a917..f947292 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -143,6 +143,10 @@ static const struct min_max_quirk min_max_pnpid_table[] = { (const char * const []){"LEN2001", NULL}, 1024, 5022, 2508, 4832 }, + { + (const char * const []){"LEN2006", NULL}, + 1264, 5675, 1171, 4688 + }, { } }; -- cgit v0.10.2 From 137bd11090d89b3a3ef4bdb7a6cf964ffc797517 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 7 Nov 2014 18:05:17 +0000 Subject: dmaengine: pl330: Align DMA memcpy operations to MFIFO width The algorithm used for programming the DMA Controller doesn't take into consideration the requirements of transfers that are not aligned to the bus width. This failure may result in DMA transferring one too few MFIFO entries (so too few bytes are copied) or the DMA trying to write one too many MFIFO entries and hanging because this is never provided. See "MFIFO Usage Overview" chapter in the the TRM for "CoreLink DMA Controller DMA-330", Revision r1p1. We work around these shortcomings by making sure we pick a burst size and length which ensures no bursts straddle an MFIFO entry. Signed-off-by: Jon Medhurst [squashed linker error "undefined reference to `__aeabi_uldivmod] Reported-by: kbuild test robot Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 4839bfa..8150542 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2459,16 +2459,25 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, /* Select max possible burst size */ burst = pl330->pcfg.data_bus_width / 8; - while (burst > 1) { - if (!(len % burst)) - break; + /* + * Make sure we use a burst size that aligns with all the memcpy + * parameters because our DMA programming algorithm doesn't cope with + * transfers which straddle an entry in the DMA device's MFIFO. + */ + while ((src | dst | len) & (burst - 1)) burst /= 2; - } desc->rqcfg.brst_size = 0; while (burst != (1 << desc->rqcfg.brst_size)) desc->rqcfg.brst_size++; + /* + * If burst size is smaller than bus width then make sure we only + * transfer one at a time to avoid a burst stradling an MFIFO entry. + */ + if (desc->rqcfg.brst_size * 8 < pl330->pcfg.data_bus_width) + desc->rqcfg.brst_len = 1; + desc->rqcfg.brst_len = get_burst_len(desc, len); desc->txd.flags = flags; -- cgit v0.10.2 From c27f95568d1ed2573fc9c8848d741da63ca9a34e Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 7 Nov 2014 18:05:18 +0000 Subject: dmaengine: pl330: Limit MFIFO usage for memcpy to avoid exhausting entries The MFIFO is shared by all channels so restrict each memcpy to it's fair share. This is being over cautious, but without a global view of DMA channel usage on a system it's not possible to come up with a more optimum safe limit. Signed-off-by: Jon Medhurst Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 8150542..e13b51a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2336,7 +2336,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) int burst_len; burst_len = pl330->pcfg.data_bus_width / 8; - burst_len *= pl330->pcfg.data_buf_dep; + burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan; burst_len >>= desc->rqcfg.brst_size; /* src/dst_burst_len can't be more than 16 */ -- cgit v0.10.2 From 1f0a5cbf61a54504236bbbe2c98b58e85f90e650 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Thu, 6 Nov 2014 17:20:12 +0000 Subject: dmaengine: Fix allocation size for PL330 data buffer depth. The datasheet for PL330 says that the data buffer value in the CRD register is 10bits wide. However, the value stored is "minus one", which the driver corrects for. Maximum value that the data buffer depth can have is 1024 lines, which requires 11 bits for storage. While making updates I found printing the peripheral ID as a hex value to be more useful as the datasheet shows the values that way. Signed-off-by: Liviu Dudau Signed-off-by: Vinod Koul diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e13b51a..19a9974 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -271,7 +271,7 @@ struct pl330_config { #define DMAC_MODE_NS (1 << 0) unsigned int mode; unsigned int data_bus_width:10; /* In number of bits */ - unsigned int data_buf_dep:10; + unsigned int data_buf_dep:11; unsigned int num_chan:4; unsigned int num_peri:6; u32 peri_ns; @@ -2741,7 +2741,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&adev->dev, - "Loaded driver for PL330 DMAC-%d\n", adev->periphid); + "Loaded driver for PL330 DMAC-%x\n", adev->periphid); dev_info(&adev->dev, "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n", pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan, -- cgit v0.10.2 From 2208d655a91f9879bd9a39ff9df05dd668b3512c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 14 Nov 2014 09:25:29 +0100 Subject: drm/i915: drop WaSetupGtModeTdRowDispatch:snb This reverts the regressing commit 6547fbdbfff62c99e4f7b4f985ff8b3454f33b0f Author: Daniel Vetter Date: Fri Dec 14 23:38:29 2012 +0100 drm/i915: Implement WaSetupGtModeTdRowDispatch that causes GPU hangs immediately on boot. Reported-by: Leo Wolf Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=79996 Cc: stable@vger.kernel.org (v3.8+) Signed-off-by: Daniel Vetter [Jani: amended the commit message slightly.] Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c27b614..ad2fd60 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5469,11 +5469,6 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(_3D_CHICKEN, _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB)); - /* WaSetupGtModeTdRowDispatch:snb */ - if (IS_SNB_GT1(dev)) - I915_WRITE(GEN6_GT_MODE, - _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE)); - /* WaDisable_RenderCache_OperationalFlush:snb */ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); -- cgit v0.10.2 From 0485c9dc24ec0939b42ca5104c0373297506b555 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 14 Nov 2014 10:09:49 +0100 Subject: drm/i915: Kick fbdev before vgacon It's magic, but it seems to work. This fixes a regression introduced in commit 1bb9e632a0aeee1121e652ee4dc80e5e6f14bcd2 Author: Daniel Vetter Date: Tue Jul 8 10:02:43 2014 +0200 drm/i915: Only unbind vgacon, not other console drivers My best guess is that the vga fbdev driver falls over if we rip out parts of vgacon. Hooray. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=82439 Cc: stable@vger.kernel.org (v3.16+) Reported-and-tested-by: Lv Zheng Signed-off-by: Daniel Vetter Acked-by: Chris Wilson Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 1403b01..318ade9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1670,15 +1670,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_regs; if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_kick_out_vgacon(dev_priv); + /* WARNING: Apparently we must kick fbdev drivers before vgacon, + * otherwise the vga fbdev driver falls over. */ + ret = i915_kick_out_firmware_fb(dev_priv); if (ret) { - DRM_ERROR("failed to remove conflicting VGA console\n"); + DRM_ERROR("failed to remove conflicting framebuffer drivers\n"); goto out_gtt; } - ret = i915_kick_out_firmware_fb(dev_priv); + ret = i915_kick_out_vgacon(dev_priv); if (ret) { - DRM_ERROR("failed to remove conflicting framebuffer drivers\n"); + DRM_ERROR("failed to remove conflicting VGA console\n"); goto out_gtt; } } -- cgit v0.10.2 From c1aefbdd050e1fb15e92bcaf34d95b17ea952097 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 17 Nov 2014 09:14:31 +0000 Subject: spi: Fix mapping from vmalloc-ed buffer to scatter list We can only use page_address on memory that has been mapped using kmap, when the buffer passed to the SPI has been allocated by vmalloc the page has not necessarily been mapped through kmap. This means sometimes page_address will return NULL causing the pointer we pass to sg_set_buf to be invalid. As we only call page_address so that we can pass a virtual address to sg_set_buf which will then immediately call virt_to_page on it, fix this by calling sg_set_page directly rather then relying on the sg_set_buf helper. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ebcb33d..50f20f2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -615,13 +615,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, sg_free_table(sgt); return -ENOMEM; } - sg_buf = page_address(vm_page) + - ((size_t)buf & ~PAGE_MASK); + sg_set_page(&sgt->sgl[i], vm_page, + min, offset_in_page(buf)); } else { sg_buf = buf; + sg_set_buf(&sgt->sgl[i], sg_buf, min); } - sg_set_buf(&sgt->sgl[i], sg_buf, min); buf += min; len -= min; -- cgit v0.10.2 From 97840cb67ff5ac8add836684f011fd838518d698 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 14 Nov 2014 18:14:33 +0100 Subject: netfilter: nfnetlink: fix insufficient validation in nfnetlink_bind Make sure the netlink group exists, otherwise you can trigger an out of bound array memory access from the netlink_bind() path. This splat can only be triggered only by superuser. [ 180.203600] UBSan: Undefined behaviour in ../net/netfilter/nfnetlink.c:467:28 [ 180.204249] index 9 is out of range for type 'int [9]' [ 180.204697] CPU: 0 PID: 1771 Comm: trinity-main Not tainted 3.18.0-rc4-mm1+ #122 [ 180.205365] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org +04/01/2014 [ 180.206498] 0000000000000018 0000000000000000 0000000000000009 ffff88007bdf7da8 [ 180.207220] ffffffff82b0ef5f 0000000000000092 ffffffff845ae2e0 ffff88007bdf7db8 [ 180.207887] ffffffff8199e489 ffff88007bdf7e18 ffffffff8199ea22 0000003900000000 [ 180.208639] Call Trace: [ 180.208857] dump_stack (lib/dump_stack.c:52) [ 180.209370] ubsan_epilogue (lib/ubsan.c:174) [ 180.209849] __ubsan_handle_out_of_bounds (lib/ubsan.c:400) [ 180.210512] nfnetlink_bind (net/netfilter/nfnetlink.c:467) [ 180.210986] netlink_bind (net/netlink/af_netlink.c:1483) [ 180.211495] SYSC_bind (net/socket.c:1541) Moreover, define the missing nf_tables and nf_acct multicast groups too. Reported-by: Andrey Ryabinin Signed-off-by: Pablo Neira Ayuso diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 6c5a915..13c2e17 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -47,6 +47,8 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = { [NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP, [NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP, [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, + [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES, + [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT, }; void nfnl_lock(__u8 subsys_id) @@ -464,7 +466,12 @@ static void nfnetlink_rcv(struct sk_buff *skb) static int nfnetlink_bind(int group) { const struct nfnetlink_subsystem *ss; - int type = nfnl_group2type[group]; + int type; + + if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX) + return -EINVAL; + + type = nfnl_group2type[group]; rcu_read_lock(); ss = nfnetlink_get_subsys(type); @@ -514,6 +521,9 @@ static int __init nfnetlink_init(void) { int i; + for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++) + BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE); + for (i=0; i Date: Mon, 17 Nov 2014 12:20:28 +0100 Subject: bridge: fix netfilter/NF_BR_LOCAL_OUT for own, locally generated queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ebtables on the OUTPUT chain (NF_BR_LOCAL_OUT) would not work as expected for both locally generated IGMP and MLD queries. The IP header specific filter options are off by 14 Bytes for netfilter (actual output on interfaces is fine). NF_HOOK() expects the skb->data to point to the IP header, not the ethernet one (while dev_queue_xmit() does not). Luckily there is an br_dev_queue_push_xmit() helper function already - let's just use that. Introduced by eb1d16414339a6e113d89e2cca2556005d7ce919 ("bridge: Add core IGMP snooping support") Ebtables example: $ ebtables -I OUTPUT -p IPv6 -o eth1 --logical-out br0 \ --log --log-level 6 --log-ip6 --log-prefix="~EBT: " -j DROP before (broken): ~EBT: IN= OUT=eth1 MAC source = 02:04:64:a4:39:c2 \ MAC dest = 33:33:00:00:00:01 proto = 0x86dd IPv6 \ SRC=64a4:39c2:86dd:6000:0000:0020:0001:fe80 IPv6 \ DST=0000:0000:0000:0004:64ff:fea4:39c2:ff02, \ IPv6 priority=0x3, Next Header=2 after (working): ~EBT: IN= OUT=eth1 MAC source = 02:04:64:a4:39:c2 \ MAC dest = 33:33:00:00:00:01 proto = 0x86dd IPv6 \ SRC=fe80:0000:0000:0000:0004:64ff:fea4:39c2 IPv6 \ DST=ff02:0000:0000:0000:0000:0000:0000:0001, \ IPv6 priority=0x0, Next Header=0 Signed-off-by: Linus Lüssing Acked-by: Herbert Xu Signed-off-by: Pablo Neira Ayuso diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 648d79c..c465876 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -813,10 +813,9 @@ static void __br_multicast_send_query(struct net_bridge *br, return; if (port) { - __skb_push(skb, sizeof(struct ethhdr)); skb->dev = port->dev; NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, - dev_queue_xmit); + br_dev_queue_push_xmit); } else { br_multicast_select_own_querier(br, ip, skb); netif_rx(skb); -- cgit v0.10.2 From f0acd28c87ad2a5d1b40403fdd5defda2961b2a1 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 10:44:33 +0100 Subject: ALSA: hda: Deletion of unnecessary checks before two function calls The functions kfree() and release_firmware() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ca98f52..b2d5899 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -834,8 +834,7 @@ static void snd_hda_bus_free(struct hda_bus *bus) WARN_ON(!list_empty(&bus->codec_list)); if (bus->workq) flush_workqueue(bus->workq); - if (bus->unsol) - kfree(bus->unsol); + kfree(bus->unsol); if (bus->ops.private_free) bus->ops.private_free(bus); if (bus->workq) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9ab1e63..91fa959 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1130,8 +1130,7 @@ static int azx_free(struct azx *chip) pci_disable_device(chip->pci); kfree(chip->azx_dev); #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (chip->fw) - release_firmware(chip->fw); + release_firmware(chip->fw); #endif if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { hda_display_power(false); -- cgit v0.10.2 From ae1b22658e6d3ebc6af07a225c221d84fe8cb91f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 11:28:02 +0100 Subject: ALSA: ice17xx: Deletion of unnecessary checks before the function call "snd_ac97_resume" The snd_ac97_resume() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 6525191..b039b46 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2878,8 +2878,7 @@ static int snd_ice1712_resume(struct device *dev) outw(ice->pm_saved_spdif_ctrl, ICEMT(ice, ROUTE_SPDOUT)); outw(ice->pm_saved_route, ICEMT(ice, ROUTE_PSDOUT03)); - if (ice->ac97) - snd_ac97_resume(ice->ac97); + snd_ac97_resume(ice->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index ea53167..d73da15 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2875,8 +2875,7 @@ static int snd_vt1724_resume(struct device *dev) outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG)); outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK)); - if (ice->ac97) - snd_ac97_resume(ice->ac97); + snd_ac97_resume(ice->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; -- cgit v0.10.2 From 6da95e1ea8e2492530eac9c51b293226e3f4ce94 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 12:42:16 +0100 Subject: ALSA: lola: Deletion of an unnecessary check before the function call "vfree" The vfree() function performs also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index 782f4d8..e7fe15d 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -108,8 +108,7 @@ int lola_init_pins(struct lola *chip, int dir, int *nidp) void lola_free_mixer(struct lola *chip) { - if (chip->mixer.array_saved) - vfree(chip->mixer.array_saved); + vfree(chip->mixer.array_saved); } int lola_init_mixer_widget(struct lola *chip, int nid) -- cgit v0.10.2 From c283661018e347bc72633969411974df8ec2ac92 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 13:04:14 +0100 Subject: ALSA: hdsp: Deletion of an unnecessary check before the function call "release_firmware" The release_firmware() function tests whether its argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 2eb8baf..cf5a6c8 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5307,8 +5307,7 @@ static int snd_hdsp_free(struct hdsp *hdsp) snd_hdsp_free_buffers(hdsp); - if (hdsp->firmware) - release_firmware(hdsp->firmware); + release_firmware(hdsp->firmware); vfree(hdsp->fw_uploaded); if (hdsp->iobase) -- cgit v0.10.2 From 1ea7a568c63a4735872fc091efbd22d2e4d9c972 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Nov 2014 13:35:54 +0100 Subject: ALSA: powermac: Deletion of an unnecessary check before the function call "pci_dev_put" The pci_dev_put() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 8a431bc..5a13b22 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -887,8 +887,7 @@ static int snd_pmac_free(struct snd_pmac *chip) } } - if (chip->pdev) - pci_dev_put(chip->pdev); + pci_dev_put(chip->pdev); of_node_put(chip->node); kfree(chip); return 0; -- cgit v0.10.2 From ff553ea1a3d5b903becda790eeb6bd3debf239f9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 14 Nov 2014 19:54:49 +0100 Subject: clk: at91: usb: fix at91rm9200 round and set rate at91rm9200_clk_usb_set_rate might fail depending on the requested rate, because the parent_rate / rate remainder is not necessarily zero. Moreover, when rounding down the calculated rate we might alter the divisor calculation and end up with an invalid divisor. To solve those problems, accept a non zero remainder, and always round division to the closest result. Signed-off-by: Boris Brezillon Reported-by: Andreas Henriksson Tested-by: Andreas Henriksson Acked-by: Nicolas Ferre Signed-off-by: Michael Turquette diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 24b5b02..5b3b63c 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -253,7 +253,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, tmp_parent_rate = rate * usb->divisors[i]; tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate); - tmprate = tmp_parent_rate / usb->divisors[i]; + tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); if (tmprate < rate) tmpdiff = rate - tmprate; else @@ -281,10 +281,10 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, struct at91_pmc *pmc = usb->pmc; unsigned long div; - if (!rate || parent_rate % rate) + if (!rate) return -EINVAL; - div = parent_rate / rate; + div = DIV_ROUND_CLOSEST(parent_rate, rate); for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { -- cgit v0.10.2 From 69daf75aafc8410ef046c70be65c71f2dd4e08f9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 17 Nov 2014 14:16:56 +0100 Subject: clk: at91: usb: fix at91sam9x5 recalc, round and set rate First check for rate == 0 in set_rate and round_rate to avoid div by zero. Then, in order to get the closest rate, round all divisions to the closest result instead of rounding them down. Signed-off-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Michael Turquette diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 5b3b63c..a23ac0c 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -52,29 +52,26 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw, tmp = pmc_read(pmc, AT91_PMC_USB); usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; - return parent_rate / (usbdiv + 1); + + return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); } static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long div; - unsigned long bestrate; - unsigned long tmp; + + if (!rate) + return -EINVAL; if (rate >= *parent_rate) return *parent_rate; - div = *parent_rate / rate; - if (div >= SAM9X5_USB_MAX_DIV) - return *parent_rate / (SAM9X5_USB_MAX_DIV + 1); - - bestrate = *parent_rate / div; - tmp = *parent_rate / (div + 1); - if (bestrate - rate > rate - tmp) - bestrate = tmp; + div = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (div > SAM9X5_USB_MAX_DIV + 1) + div = SAM9X5_USB_MAX_DIV + 1; - return bestrate; + return DIV_ROUND_CLOSEST(*parent_rate, div); } static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) @@ -106,9 +103,13 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, u32 tmp; struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw); struct at91_pmc *pmc = usb->pmc; - unsigned long div = parent_rate / rate; + unsigned long div; - if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV) + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > SAM9X5_USB_MAX_DIV + 1 || !div) return -EINVAL; tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV; -- cgit v0.10.2 From 9a6cb70f40b0268297024949eb0a2689e3b7769b Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 10 Oct 2014 16:57:24 +0300 Subject: clk: qcom: Fix duplicate rbcpr clock name There is a duplication in a clock name for apq8084 platform that causes the following warning: "RBCPR_CLK_SRC" redefined Resolve this by adding a MMSS_ prefix to this clock and making its name coherent with msm8974 platform. Fixes: 2b46cd23a5a2 ("clk: qcom: Add APQ8084 Multimedia Clock Controller (MMCC) support") Signed-off-by: Georgi Djakov Reviewed-by: Stephen Boyd Signed-off-by: Michael Turquette diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index dab988a..157139a 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -3122,7 +3122,7 @@ static struct clk_regmap *mmcc_apq8084_clocks[] = { [ESC1_CLK_SRC] = &esc1_clk_src.clkr, [HDMI_CLK_SRC] = &hdmi_clk_src.clkr, [VSYNC_CLK_SRC] = &vsync_clk_src.clkr, - [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, + [MMSS_RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, [MAPLE_CLK_SRC] = &maple_clk_src.clkr, [VDP_CLK_SRC] = &vdp_clk_src.clkr, diff --git a/include/dt-bindings/clock/qcom,mmcc-apq8084.h b/include/dt-bindings/clock/qcom,mmcc-apq8084.h index a929f86..d72b5b3 100644 --- a/include/dt-bindings/clock/qcom,mmcc-apq8084.h +++ b/include/dt-bindings/clock/qcom,mmcc-apq8084.h @@ -60,7 +60,7 @@ #define ESC1_CLK_SRC 43 #define HDMI_CLK_SRC 44 #define VSYNC_CLK_SRC 45 -#define RBCPR_CLK_SRC 46 +#define MMSS_RBCPR_CLK_SRC 46 #define RBBMTIMER_CLK_SRC 47 #define MAPLE_CLK_SRC 48 #define VDP_CLK_SRC 49 -- cgit v0.10.2 From e6d5e7d90be92cee626d7ec16ca9b06f1eed710b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 14 Nov 2014 15:32:09 +0000 Subject: clk-divider: Fix READ_ONLY when divider > 1 Commit 79c6ab509558 (clk: divider: add CLK_DIVIDER_READ_ONLY flag) in v3.16 introduced the CLK_DIVIDER_READ_ONLY flag which caused the recalc_rate() and round_rate() clock callbacks to be omitted. However using this flag has the unfortunate side effect of causing the clock recalculation code when a clock rate change is attempted to always treat it as a pass-through clock, i.e. with a fixed divide of 1, which may not be the case. Child clock rates are then recalculated using the wrong parent rate. Therefore instead of dropping the recalc_rate() and round_rate() callbacks, alter clk_divider_bestdiv() to always report the current divider as the best divider so that it is never altered. For me the read only clock was the system clock, which divided the PLL rate by 2, from which both the UART and the SPI clocks were divided. Initial setting of the UART rate set it correctly, but when the SPI clock was set, the other child clocks were miscalculated. The UART clock was recalculated using the PLL rate as the parent rate, resulting in a UART new_rate of double what it should be, and a UART which spewed forth garbage when the rate changes were propagated. Signed-off-by: James Hogan Cc: Thomas Abraham Cc: Tomasz Figa Cc: Max Schwarz Cc: # v3.16+ Acked-by: Haojian Zhuang Signed-off-by: Michael Turquette diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 18a9de2..c0a842b 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -263,6 +263,14 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!rate) rate = 1; + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + bestdiv = readl(divider->reg) >> divider->shift; + bestdiv &= div_mask(divider); + bestdiv = _get_div(divider, bestdiv); + return bestdiv; + } + maxdiv = _get_maxdiv(divider); if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { @@ -361,11 +369,6 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); -const struct clk_ops clk_divider_ro_ops = { - .recalc_rate = clk_divider_recalc_rate, -}; -EXPORT_SYMBOL_GPL(clk_divider_ro_ops); - static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -391,10 +394,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, } init.name = name; - if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) - init.ops = &clk_divider_ro_ops; - else - init.ops = &clk_divider_ops; + init.ops = &clk_divider_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 1e68bff..880a266 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -90,9 +90,7 @@ static struct clk *rockchip_clk_register_branch(const char *name, div->width = div_width; div->lock = lock; div->table = div_table; - div_ops = (div_flags & CLK_DIVIDER_READ_ONLY) - ? &clk_divider_ro_ops - : &clk_divider_ops; + div_ops = &clk_divider_ops; } clk = clk_register_composite(NULL, name, parent_names, num_parents, diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index be21af1..2839c63 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -352,7 +352,6 @@ struct clk_divider { #define CLK_DIVIDER_READ_ONLY BIT(5) extern const struct clk_ops clk_divider_ops; -extern const struct clk_ops clk_divider_ro_ops; struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, -- cgit v0.10.2 From dcf3d458304aafda3d12413ade39fdf19740dbc3 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 7 Oct 2014 01:07:57 +0200 Subject: clk: pxa: fix pxa27x CCCR bit usage Trivial fix to check the A bit of CCCR for memory frequency calculations, where the shift of the bit index was missing, triggering a wrong calculation of memory frequency. Signed-off-by: Robert Jarzmik Signed-off-by: Michael Turquette diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index b345cc7..88b9fe1 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -322,7 +322,7 @@ static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw, unsigned long ccsr = CCSR; osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - a = cccr & CCCR_A_BIT; + a = cccr & (1 << CCCR_A_BIT); l = ccsr & CCSR_L_MASK; if (osc_forced || a) @@ -341,7 +341,7 @@ static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw) unsigned long ccsr = CCSR; osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - a = cccr & CCCR_A_BIT; + a = cccr & (1 << CCCR_A_BIT); if (osc_forced) return PXA_MEM_13Mhz; if (a) -- cgit v0.10.2 From daad1660285a967b5da363e8de264e86b4a496a0 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 4 Nov 2014 15:22:50 -0800 Subject: ath9k: fix regression in bssidmask calculation The commit that went into 3.17: ath9k: Summarize hw state per channel context Group and set hw state (opmode, primary_sta, beacon conf) per channel context instead of whole list of vifs. This would allow each channel context to run in different mode (STA/AP). Signed-off-by: Felix Fietkau Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville broke multi-vif configuration due to not properly calculating the bssid mask. The test case that caught this was: create wlan0 and sta0-4 (6 total), not sure how much that matters. associate all 6 (works fine) disconnect 5 of them, leaving sta0 up Start trying to bring up the other 5 one at a time. It will fail, with iw events looking like this (in these logs, several sta are trying to come up, but symptom is the same with just one) The patch causing the regression made quite a few changes, but the part I think caused this particular problem was not recalculating the bssid mask when adding and removing interfaces. Re-adding those calls fixes my test case. Fix bad comment as well. Signed-off-by: Ben Greear Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 30c66df..4f18a6b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -974,9 +974,8 @@ void ath9k_calculate_iter_data(struct ath_softc *sc, struct ath_vif *avp; /* - * Pick the MAC address of the first interface as the new hardware - * MAC address. The hardware will use it together with the BSSID mask - * when matching addresses. + * The hardware will use primary station addr together with the + * BSSID mask when matching addresses. */ memset(iter_data, 0, sizeof(*iter_data)); memset(&iter_data->mask, 0xff, ETH_ALEN); @@ -1205,6 +1204,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, list_add_tail(&avp->list, &avp->chanctx->vifs); } + ath9k_calculate_summary_state(sc, avp->chanctx); + ath9k_assign_hw_queues(hw, vif); an->sc = sc; @@ -1274,6 +1275,8 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ath_tx_node_cleanup(sc, &avp->mcast_node); + ath9k_calculate_summary_state(sc, avp->chanctx); + mutex_unlock(&sc->mutex); } -- cgit v0.10.2 From 8180bd47b043507568056f74f69b6a5abea26514 Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Wed, 12 Nov 2014 21:33:34 -0500 Subject: brcmfmac: kill URB when request timed out Kill the submitted URB in brcmf_usb_dl_cmd if the request timed out. This assures the URB is never submitted twice. It also prevents a possible use-after-free of the URB transfer buffer if a timeout occurs. Signed-off-by: Mathy Vanhoef Acked-by: Arend van Spriel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index dc13591..875d114 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -669,10 +669,12 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, goto finalize; } - if (!brcmf_usb_ioctl_resp_wait(devinfo)) + if (!brcmf_usb_ioctl_resp_wait(devinfo)) { + usb_kill_urb(devinfo->ctl_urb); ret = -ETIMEDOUT; - else + } else { memcpy(buffer, tmpbuf, buflen); + } finalize: kfree(tmpbuf); -- cgit v0.10.2 From 4c69f05eaa428e37890daf88b86a567ce615570b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:12:21 -0800 Subject: brcmfmac: fix error handling of irq_of_parse_and_map Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Cc: stable@vger.kernel.org # v3.17 Acked-by: Arend van Spriel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index f05f527..927bffd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -40,8 +40,8 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) return; irq = irq_of_parse_and_map(np, 0); - if (irq < 0) { - brcmf_err("interrupt could not be mapped: err=%d\n", irq); + if (!irq) { + brcmf_err("interrupt could not be mapped\n"); devm_kfree(dev, sdiodev->pdata); return; } -- cgit v0.10.2 From d7ce4377494adfaf8afb15ecf4f07d399bbf13d9 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Fri, 14 Nov 2014 13:51:22 +0800 Subject: powerpc/fsl_msi: mark the msi cascade handler IRQF_NO_THREAD The commit 543c043cbae7 ("powerpc/fsl_msi: change the irq handler from chained to normal") changes the msi cascade handler from chained to normal. Since cascade handler must run in hard interrupt context, this will cause kernel panic if we force threading of all the interrupt handler via kernel command parameter 'threadirqs'. So mark the irq handler IRQF_NO_THREAD explicitly. Signed-off-by: Kevin Hao Signed-off-by: Scott Wood diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index de40b48..da08ed0 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -361,7 +361,7 @@ static int fsl_msi_setup_hwirq(struct fsl_msi *msi, struct platform_device *dev, cascade_data->virq = virt_msir; msi->cascade_array[irq_index] = cascade_data; - ret = request_irq(virt_msir, fsl_msi_cascade, 0, + ret = request_irq(virt_msir, fsl_msi_cascade, IRQF_NO_THREAD, "fsl-msi-cascade", cascade_data); if (ret) { dev_err(&dev->dev, "failed to request_irq(%d), ret = %d\n", -- cgit v0.10.2 From 5247a589c24022ab34e780039cc8000c48f2035e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rper?= Date: Fri, 31 Oct 2014 07:33:54 +0100 Subject: can: dev: avoid calling kfree_skb() from interrupt context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ikfree_skb() is Called in can_free_echo_skb(), which might be called from (TX Error) interrupt, which triggers the folloing warning: [ 1153.360705] ------------[ cut here ]------------ [ 1153.360715] WARNING: CPU: 0 PID: 31 at net/core/skbuff.c:563 skb_release_head_state+0xb9/0xd0() [ 1153.360772] Call Trace: [ 1153.360778] [] dump_stack+0x41/0x52 [ 1153.360782] [] warn_slowpath_common+0x7e/0xa0 [ 1153.360784] [] ? skb_release_head_state+0xb9/0xd0 [ 1153.360786] [] ? skb_release_head_state+0xb9/0xd0 [ 1153.360788] [] warn_slowpath_null+0x22/0x30 [ 1153.360791] [] skb_release_head_state+0xb9/0xd0 [ 1153.360793] [] skb_release_all+0x10/0x30 [ 1153.360795] [] kfree_skb+0x36/0x80 [ 1153.360799] [] ? can_free_echo_skb+0x28/0x40 [can_dev] [ 1153.360802] [] can_free_echo_skb+0x28/0x40 [can_dev] [ 1153.360805] [] esd_pci402_interrupt+0x34c/0x57a [esd402] [ 1153.360809] [] handle_irq_event_percpu+0x35/0x180 [ 1153.360811] [] ? handle_irq_event_percpu+0xa3/0x180 [ 1153.360813] [] handle_irq_event+0x31/0x50 [ 1153.360816] [] handle_fasteoi_irq+0x6f/0x120 [ 1153.360818] [] ? handle_edge_irq+0x110/0x110 [ 1153.360822] [] handle_irq+0x71/0x90 [ 1153.360823] [] do_IRQ+0x3c/0xd0 [ 1153.360829] [] common_interrupt+0x2c/0x34 [ 1153.360834] [] ? finish_task_switch+0x47/0xf0 [ 1153.360836] [] __schedule+0x35b/0x7e0 [ 1153.360839] [] ? console_unlock+0x2c4/0x4d0 [ 1153.360842] [] ? n_tty_receive_buf_common+0x890/0x890 [ 1153.360845] [] ? process_one_work+0x196/0x370 [ 1153.360847] [] schedule+0x23/0x60 [ 1153.360849] [] worker_thread+0x161/0x460 [ 1153.360852] [] ? __wake_up_locked+0x1f/0x30 [ 1153.360854] [] ? rescuer_thread+0x2f0/0x2f0 [ 1153.360856] [] kthread+0xa1/0xc0 [ 1153.360859] [] ret_from_kernel_thread+0x21/0x30 [ 1153.360861] [] ? kthread_create_on_node+0x110/0x110 [ 1153.360863] ---[ end trace 5ff83639cbb74b35 ]--- This patch replaces the kfree_skb() by dev_kfree_skb_any(). Signed-off-by: Thomas Körper Cc: linux-stable Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 02492d2..509d5e0 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -382,7 +382,7 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx) BUG_ON(idx >= priv->echo_skb_max); if (priv->echo_skb[idx]) { - kfree_skb(priv->echo_skb[idx]); + dev_kfree_skb_any(priv->echo_skb[idx]); priv->echo_skb[idx] = NULL; } } -- cgit v0.10.2 From 67b5909edccfe3ea3b85b1d96284d2c53e3fd47c Mon Sep 17 00:00:00 2001 From: Roman Fietze Date: Mon, 20 Oct 2014 10:32:42 +0200 Subject: can: dev: fix typo CIA -> CiA, CAN in Automation This patch fixes a typo in CAN's dev.c: CIA -> CiA which stands for CAN in Automation. Signed-off-by: Roman Fietze Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 509d5e0..2cfe501 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -110,7 +110,7 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt, long rate; u64 v64; - /* Use CIA recommended sample points */ + /* Use CiA recommended sample points */ if (bt->sample_point) { sampl_pt = bt->sample_point; } else { -- cgit v0.10.2 From 98e69016a11b2b9398bea668442193b3b362cd43 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 7 Nov 2014 16:45:12 +0800 Subject: can: dev: add can_is_canfd_skb() API The CAN device drivers can use can_is_canfd_skb() to check if the frame to send is on CAN FD mode or normal CAN mode. Acked-by: Oliver Hartkopp Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 6992afc..b37ea95 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -99,6 +99,12 @@ inval_skb: return 1; } +static inline bool can_is_canfd_skb(const struct sk_buff *skb) +{ + /* the CAN specific type of skb is identified by its data length */ + return skb->len == CANFD_MTU; +} + /* get data length from can_dlc with sanitized can_dlc */ u8 can_dlc2len(u8 can_dlc); -- cgit v0.10.2 From efbd50d2f62fc1f69a3dcd153e63ba28cc8eb27f Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 11 Oct 2014 00:31:07 +0400 Subject: can: esd_usb2: fix memory leak on disconnect It seems struct esd_usb2 dev is not deallocated on disconnect. The patch adds the missing deallocation. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Matthias Fuchs Cc: linux-stable Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index b7c9e8b..7a90075 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -1143,6 +1143,7 @@ static void esd_usb2_disconnect(struct usb_interface *intf) } } unlink_all_urbs(dev); + kfree(dev); } } -- cgit v0.10.2 From 9c4b19a07dddda3ba35a2eb9b4134d485908e2f5 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 17 Nov 2014 23:17:02 +0800 Subject: spi: sirf: fix word width configuration commit 8c328a262f ("spi: sirf: Avoid duplicate code in various bits_per_word cases") is wrong in setting data width register of fifo is not right, it should use sspi->word_width >> 1 to set related bits. According to hardware spec, the mapping between register value and data width: 0 - byte 1 - WORD 2 - DWORD Fixes: 8c328a262f ("spi: sirf: Avoid duplicate code in various bits_per_word cases") is wrong in setting data width register of Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 39e2c0a..f63de78 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -562,9 +562,9 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); if (!(spi->mode & SPI_CS_HIGH)) regval |= SIRFSOC_SPI_CS_IDLE_STAT; -- cgit v0.10.2 From 4e2061b1e1f6b8e698b36a6518b6f8c15edc4547 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 18 Nov 2014 19:17:06 +0530 Subject: can: remove unused variable these variable were only assigned some values, but then never reused again. so they are safe to be removed. Signed-off-by: Sudip Mukherjee Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c index 8ff3424..15c00fa 100644 --- a/drivers/net/can/sja1000/kvaser_pci.c +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -214,7 +214,7 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, struct net_device *dev; struct sja1000_priv *priv; struct kvaser_pci *board; - int err, init_step; + int err; dev = alloc_sja1000dev(sizeof(struct kvaser_pci)); if (dev == NULL) @@ -235,7 +235,6 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, if (channel == 0) { board->xilinx_ver = ioread8(board->res_addr + XILINX_VERINT) >> 4; - init_step = 2; /* Assert PTADR# - we're in passive mode so the other bits are not important */ @@ -264,8 +263,6 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel, priv->irq_flags = IRQF_SHARED; dev->irq = pdev->irq; - init_step = 4; - dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n", priv->reg_base, board->conf_addr, dev->irq); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 00f2534..29d3f09 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -434,10 +434,9 @@ static void ems_usb_read_bulk_callback(struct urb *urb) if (urb->actual_length > CPC_HEADER_SIZE) { struct ems_cpc_msg *msg; u8 *ibuf = urb->transfer_buffer; - u8 msg_count, again, start; + u8 msg_count, start; msg_count = ibuf[0] & ~0x80; - again = ibuf[0] & 0x80; start = CPC_HEADER_SIZE; diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 7a90075..c063a54 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -464,7 +464,6 @@ static void esd_usb2_write_bulk_callback(struct urb *urb) { struct esd_tx_urb_context *context = urb->context; struct esd_usb2_net_priv *priv; - struct esd_usb2 *dev; struct net_device *netdev; size_t size = sizeof(struct esd_usb2_msg); @@ -472,7 +471,6 @@ static void esd_usb2_write_bulk_callback(struct urb *urb) priv = context->priv; netdev = priv->netdev; - dev = priv->usb2; /* free up our allocated buffer */ usb_free_coherent(urb->dev, size, -- cgit v0.10.2 From fb3ec7ba5a665c280f7299a36460449038fc1083 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 18 Nov 2014 19:17:07 +0530 Subject: can: xilinx_can: fix comparison of unsigned variable The variable err was of the type u32. It was being compared with < 0, and being an unsigned variable the comparison would have been always false. Moreover, err was getting the return value from set_reset_mode() and xcan_set_bittiming(), and both are returning int. Signed-off-by: Sudip Mukherjee Reviewed-by: Michal Simek Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 5e8b560..47b2f80 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -300,7 +300,8 @@ static int xcan_set_bittiming(struct net_device *ndev) static int xcan_chip_start(struct net_device *ndev) { struct xcan_priv *priv = netdev_priv(ndev); - u32 err, reg_msr, reg_sr_mask; + u32 reg_msr, reg_sr_mask; + int err; unsigned long timeout; /* Check if it is in reset mode */ -- cgit v0.10.2 From 92593a035ee6a81f50b54ab34b23b1c1319ee413 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 18 Nov 2014 13:16:13 +0100 Subject: can: xilinx_can: add .ndo_change_mtu function Use common can_change_mtu function. Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 47b2f80..8a998e3 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -962,6 +962,7 @@ static const struct net_device_ops xcan_netdev_ops = { .ndo_open = xcan_open, .ndo_stop = xcan_close, .ndo_start_xmit = xcan_start_xmit, + .ndo_change_mtu = can_change_mtu, }; /** -- cgit v0.10.2 From ca976d6af4e50899f4e840e8017ec29d8fe6b50e Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 18 Nov 2014 13:16:13 +0100 Subject: can: rcar_can: add .ndo_change_mtu function Use common can_change_mtu function. Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c index 1abe133..9718248 100644 --- a/drivers/net/can/rcar_can.c +++ b/drivers/net/can/rcar_can.c @@ -628,6 +628,7 @@ static const struct net_device_ops rcar_can_netdev_ops = { .ndo_open = rcar_can_open, .ndo_stop = rcar_can_close, .ndo_start_xmit = rcar_can_start_xmit, + .ndo_change_mtu = can_change_mtu, }; static void rcar_can_rx_pkt(struct rcar_can_priv *priv) -- cgit v0.10.2 From 5f217f905bc5e9d609d0aac830736bcfc087c7f5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 18 Nov 2014 23:59:40 +0900 Subject: ALSA: firewire-lib: fix kerneldoc errors Complete missing parameters, correct wrong reference, and add an explaination about the differences between the latest specification and our implementation. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 4823c08..e6e8926 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -23,7 +23,7 @@ * corresponds to the end of event in the packet. Out of IEC 61883. * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. * The value of data_block_quadlets is used instead of reported value. - * @SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is + * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is * skipped for detecting discontinuity. * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first * packet is not continuous from an initial value. @@ -43,7 +43,27 @@ enum cip_flags { }; /** - * enum cip_sfc - a stream's sample rate + * enum cip_sfc - supported Sampling Frequency Codes (SFCs) + * @CIP_SFC_32000: 32,000 data blocks + * @CIP_SFC_44100: 44,100 data blocks + * @CIP_SFC_48000: 48,000 data blocks + * @CIP_SFC_88200: 88,200 data blocks + * @CIP_SFC_96000: 96,000 data blocks + * @CIP_SFC_176400: 176,400 data blocks + * @CIP_SFC_192000: 192,000 data blocks + * @CIP_SFC_COUNT: the number of supported SFCs + * + * These values are used to show nominal Sampling Frequency Code in + * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, + * this code means the number of events per second. Actually the code + * represents the number of data blocks transferred per second in an AMDTP + * stream. + * + * In IEC 61883-6:2005, some extensions were added to support more types of + * data such as 'One Bit LInear Audio', therefore the meaning of SFC became + * different depending on the types. + * + * Currently our implementation is compatible with IEC 61883-6:2002. */ enum cip_sfc { CIP_SFC_32000 = 0, diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index ba8df5a..ae3bc19 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -114,6 +114,7 @@ static int pcr_modify(struct cmp_connection *c, * cmp_connection_init - initializes a connection manager * @c: the connection manager to initialize * @unit: a unit of the target device + * @direction: input or output * @pcr_index: the index of the iPCR/oPCR on the target device */ int cmp_connection_init(struct cmp_connection *c, @@ -154,6 +155,7 @@ EXPORT_SYMBOL(cmp_connection_init); /** * cmp_connection_check_used - check connection is already esablished or not * @c: the connection manager to be checked + * @used: the pointer to store the result of checking the connection */ int cmp_connection_check_used(struct cmp_connection *c, bool *used) { -- cgit v0.10.2 From f2a306c29d024193b1272cd014108882f7887a9e Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 26 Sep 2014 00:26:27 +0200 Subject: devicetree: bindings: add sandisk to the vendor prefixes Add sandisk to the list of vendors. This prefix should be used also for companies absorbed by Sandisk, like M-Systems. Signed-off-by: Robert Jarzmik Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 723999d..3de8030 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -127,6 +127,7 @@ renesas Renesas Electronics Corporation ricoh Ricoh Co. Ltd. rockchip Fuzhou Rockchip Electronics Co., Ltd samsung Samsung Semiconductor +sandisk Sandisk Corporation sbs Smart Battery System schindler Schindler seagate Seagate Technology PLC -- cgit v0.10.2 From fb86b97300d930b57471068720c52bfa8622eab7 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 18 Nov 2014 10:46:57 +0100 Subject: x86, microcode: Update BSPs microcode on resume In the situation when we apply early microcode but do *not* apply late microcode, we fail to update the BSP's microcode on resume because we haven't initialized the uci->mc microcode pointer. So, in order to alleviate that, we go and dig out the stashed microcode patch during early boot. It is basically the same thing that is done on the APs early during boot so do that too here. Tested-by: alex.schnaidt@gmail.com Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=88001 Cc: Henrique de Moraes Holschuh Cc: Fenghua Yu Cc: # v3.9 Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/20141118094657.GA6635@pd.tnic Signed-off-by: Thomas Gleixner diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index dd9d619..2ce9051 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -465,6 +465,14 @@ static void mc_bp_resume(void) if (uci->valid && uci->mc) microcode_ops->apply_microcode(cpu); + else if (!uci->mc) + /* + * We might resume and not have applied late microcode but still + * have a newer patch stashed from the early loader. We don't + * have it in uci->mc so we have to load it the same way we're + * applying patches early on the APs. + */ + load_ucode_ap(); } static struct syscore_ops mc_syscore_ops = { -- cgit v0.10.2 From 45e2a9d4701d8c624d4a4bcdd1084eae31e92f58 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 14 Nov 2014 11:47:37 -0800 Subject: x86, mm: Set NX across entire PMD at boot When setting up permissions on kernel memory at boot, the end of the PMD that was split from bss remained executable. It should be NX like the rest. This performs a PMD alignment instead of a PAGE alignment to get the correct span of memory. Before: ---[ High Kernel Mapping ]--- ... 0xffffffff8202d000-0xffffffff82200000 1868K RW GLB NX pte 0xffffffff82200000-0xffffffff82c00000 10M RW PSE GLB NX pmd 0xffffffff82c00000-0xffffffff82df5000 2004K RW GLB NX pte 0xffffffff82df5000-0xffffffff82e00000 44K RW GLB x pte 0xffffffff82e00000-0xffffffffc0000000 978M pmd After: ---[ High Kernel Mapping ]--- ... 0xffffffff8202d000-0xffffffff82200000 1868K RW GLB NX pte 0xffffffff82200000-0xffffffff82e00000 12M RW PSE GLB NX pmd 0xffffffff82e00000-0xffffffffc0000000 978M pmd [ tglx: Changed it to roundup(_brk_end, PMD_SIZE) and added a comment. We really should unmap the reminder along with the holes caused by init,initdata etc. but thats a different issue ] Signed-off-by: Kees Cook Cc: Andy Lutomirski Cc: Toshi Kani Cc: Yasuaki Ishimatsu Cc: David Vrabel Cc: Wang Nan Cc: Yinghai Lu Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20141114194737.GA3091@www.outflux.net Signed-off-by: Thomas Gleixner diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 4cb8763..4e5dfec 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1123,7 +1123,7 @@ void mark_rodata_ro(void) unsigned long end = (unsigned long) &__end_rodata_hpage_align; unsigned long text_end = PFN_ALIGN(&__stop___ex_table); unsigned long rodata_end = PFN_ALIGN(&__end_rodata); - unsigned long all_end = PFN_ALIGN(&_end); + unsigned long all_end; printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", (end - start) >> 10); @@ -1134,7 +1134,16 @@ void mark_rodata_ro(void) /* * The rodata/data/bss/brk section (but not the kernel text!) * should also be not-executable. + * + * We align all_end to PMD_SIZE because the existing mapping + * is a full PMD. If we would align _brk_end to PAGE_SIZE we + * split the PMD and the reminder between _brk_end and the end + * of the PMD will remain mapped executable. + * + * Any PMD which was setup after the one which covers _brk_end + * has been zapped already via cleanup_highmem(). */ + all_end = roundup((unsigned long)_brk_end, PMD_SIZE); set_memory_nx(rodata_start, (all_end - rodata_start) >> PAGE_SHIFT); rodata_test(); -- cgit v0.10.2 From 70b61e362187b5fccac206506d402f3424e3e749 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 17 Nov 2014 16:16:04 -0800 Subject: x86, kaslr: Handle Gold linker for finding bss/brk When building with the Gold linker, the .bss and .brk areas of vmlinux are shown as consecutive instead of having the same file offset. Allow for either state, as long as things add up correctly. Fixes: e6023367d779 ("x86, kaslr: Prevent .bss from overlaping initrd") Reported-by: Markus Trippelsdorf Signed-off-by: Kees Cook Cc: Junjie Mao Link: http://lkml.kernel.org/r/20141118001604.GA25045@www.outflux.net Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner diff --git a/arch/x86/tools/calc_run_size.pl b/arch/x86/tools/calc_run_size.pl index 0b0b124..23210ba 100644 --- a/arch/x86/tools/calc_run_size.pl +++ b/arch/x86/tools/calc_run_size.pl @@ -19,7 +19,16 @@ while (<>) { if ($file_offset == 0) { $file_offset = $offset; } elsif ($file_offset != $offset) { - die ".bss and .brk lack common file offset\n"; + # BFD linker shows the same file offset in ELF. + # Gold linker shows them as consecutive. + next if ($file_offset + $mem_size == $offset + $size); + + printf STDERR "file_offset: 0x%lx\n", $file_offset; + printf STDERR "mem_size: 0x%lx\n", $mem_size; + printf STDERR "offset: 0x%lx\n", $offset; + printf STDERR "size: 0x%lx\n", $size; + + die ".bss and .brk are non-contiguous\n"; } } } -- cgit v0.10.2 From 27b3383a1432127bfcf9f8a63bf184ff4d866141 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 22 Oct 2014 11:49:01 +0200 Subject: of: Spelling s/stucture/structure/ Signed-off-by: Geert Uytterhoeven Cc: Grant Likely Cc: Rob Herring Signed-off-by: Rob Herring diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index f297891..d4994177 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -247,7 +247,7 @@ void of_node_release(struct kobject *kobj) * @allocflags: Allocation flags (typically pass GFP_KERNEL) * * Copy a property by dynamically allocating the memory of both the - * property stucture and the property name & contents. The property's + * property structure and the property name & contents. The property's * flags have the OF_DYNAMIC bit set so that we can differentiate between * dynamically allocated properties and not. * Returns the newly allocated property or NULL on out of memory error. -- cgit v0.10.2 From 66865de4314caca30598244b86817e774c188afa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 1 Nov 2014 17:35:31 -0600 Subject: of/irq: Drop obsolete 'interrupts' vs 'interrupts-extended' text a9ecdc0fdc54 ("of/irq: Fix lookup to use 'interrupts-extended' property first") updated the description to say that: - Both 'interrupts' and 'interrupts-extended' may be present - Software should prefer 'interrupts-extended' - Software that doesn't comprehend 'interrupts-extended' may use 'interrupts' But there is still a paragraph at the end that prohibits having both and says 'interrupts' should be preferred. Remove the contradictory text. Fixes: a9ecdc0fdc54 ("of/irq: Fix lookup to use 'interrupts-extended' property first") Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.13+ Acked-by: Brian Norris Acked-by: Mark Rutland Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt index ce6a1a0..8a3c408 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt @@ -30,10 +30,6 @@ should only be used when a device has multiple interrupt parents. Example: interrupts-extended = <&intc1 5 1>, <&intc2 1 0>; -A device node may contain either "interrupts" or "interrupts-extended", but not -both. If both properties are present, then the operating system should log an -error and use only the data in "interrupts". - 2) Interrupt controller nodes ----------------------------- -- cgit v0.10.2 From ab74d00a39f70e1bc34a01322bb59f3750ca7a8c Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sun, 9 Nov 2014 00:55:47 -0800 Subject: of: Fix crash if an earlycon driver is not found __earlycon_of_table_sentinel.compatible is a char[128], not a pointer, so it will never be NULL. Checking it against NULL causes the match loop to run past the end of the array, and eventually match a bogus entry, under the following conditions: - Kernel command line specifies "earlycon" with no parameters - DT has a stdout-path pointing to a UART node - The UART driver doesn't use OF_EARLYCON_DECLARE (or maybe the console driver is compiled out) Fix this by checking to see if match->compatible is a non-empty string. Signed-off-by: Kevin Cernekee Cc: # 3.16+ Signed-off-by: Rob Herring diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index d1ffca8..30e97bc 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -773,7 +773,7 @@ int __init early_init_dt_scan_chosen_serial(void) if (offset < 0) return -ENODEV; - while (match->compatible) { + while (match->compatible[0]) { unsigned long addr; if (fdt_node_check_compatible(fdt, offset, match->compatible)) { match++; -- cgit v0.10.2 From 9b6eab07588c2de102423fe99c875fc4bfda2508 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Sun, 9 Nov 2014 01:37:34 +0300 Subject: devicetree: vendor-prefixes.txt: fix whitespace Signed-off-by: Antony Pavlov Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 3de8030..f9c7e77 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -139,7 +139,7 @@ silergy Silergy Corp. sirf SiRF Technology, Inc. sitronix Sitronix Technology Corporation smsc Standard Microsystems Corporation -snps Synopsys, Inc. +snps Synopsys, Inc. solidrun SolidRun sony Sony Corporation spansion Spansion Inc. -- cgit v0.10.2 From 746c9e9f92dde2789908e51a354ba90a1962a2eb Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 14 Nov 2014 17:55:03 +1100 Subject: of/base: Fix PowerPC address parsing hack We have a historical hack that treats missing ranges properties as the equivalent of an empty one. This is needed for ancient PowerMac "bad" device-trees, and shouldn't be enabled for any other PowerPC platform, otherwise we get some nasty layout of devices in sysfs or even duplication when a set of otherwise identically named devices is created multiple times under a different parent node with no ranges property. This fix is needed for the PowerNV i2c busses to be exposed properly and will fix a number of other embedded cases. Signed-off-by: Benjamin Herrenschmidt CC: Acked-by: Grant Likely Signed-off-by: Rob Herring diff --git a/drivers/of/address.c b/drivers/of/address.c index afdb782..06af494 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -450,6 +450,21 @@ static struct of_bus *of_match_bus(struct device_node *np) return NULL; } +static int of_empty_ranges_quirk(void) +{ + if (IS_ENABLED(CONFIG_PPC)) { + /* To save cycles, we cache the result */ + static int quirk_state = -1; + + if (quirk_state < 0) + quirk_state = + of_machine_is_compatible("Power Macintosh") || + of_machine_is_compatible("MacRISC"); + return quirk_state; + } + return false; +} + static int of_translate_one(struct device_node *parent, struct of_bus *bus, struct of_bus *pbus, __be32 *addr, int na, int ns, int pna, const char *rprop) @@ -475,12 +490,10 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * This code is only enabled on powerpc. --gcl */ ranges = of_get_property(parent, rprop, &rlen); -#if !defined(CONFIG_PPC) - if (ranges == NULL) { + if (ranges == NULL && !of_empty_ranges_quirk()) { pr_err("OF: no ranges; cannot translate\n"); return 1; } -#endif /* !defined(CONFIG_PPC) */ if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); memset(addr, 0, pna * 4); -- cgit v0.10.2 From f9cb89b63db8cb2755a5179843a0643cc284f1ef Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 14 May 2014 11:24:43 +0200 Subject: of: Add vendor prefix for Chips&Media, Inc. Chips&Media is a developer of Video Codec IP cores. Signed-off-by: Philipp Zabel [robh: fix-up alphabetical ordering] Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index f9c7e77..a065578 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -34,6 +34,7 @@ chipidea Chipidea, Inc chrp Common Hardware Reference Platform chunghwa Chunghwa Picture Tubes Ltd. cirrus Cirrus Logic, Inc. +cnm Chips&Media, Inc. cortina Cortina Systems, Inc. crystalfontz Crystalfontz America, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) -- cgit v0.10.2 From 5641c09226f401ee054e48521707fb185380e8d3 Mon Sep 17 00:00:00 2001 From: bpqw Date: Wed, 12 Nov 2014 14:26:42 +0000 Subject: devicetree: bindings: Add vendor prefix for Micron Technology, Inc. This patch is used to add vendor prefix for Micron Technology, Inc. in the vendor-prefixes.txt file. Micron Technology, Inc. is an American multinational corporation based in Boise, Idaho, best known for producing many forms of semiconductor devices. This includes DRAM, SDRAM, flash memory, eMMC and SSDs. Signed-off-by: Bean Huo [robh: cleanup commit msg formatting and company name] Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index a065578..a344ec2 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -93,6 +93,7 @@ maxim Maxim Integrated Products mediatek MediaTek Inc. micrel Micrel Inc. microchip Microchip Technology Inc. +micron Micron Technology Inc. mitsubishi Mitsubishi Electric Corporation mosaixtech Mosaix Technologies, Inc. moxa Moxa -- cgit v0.10.2 From a0e27f51ba8a04125c22a95c4d3e98297a7191de Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Thu, 6 Nov 2014 07:38:51 -0800 Subject: documentation: pinctrl bindings: Fix trivial typo 'abitrary' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A misspelled 'arbitrary' propagated to quite a few locations in the DT binding documentation for pin-controllers. Fixing by: git grep abitrary | cut -f1 -d: | xargs sed -i 's/abitrary/arbitrary/' Reported-by: Andreas Färber Signed-off-by: Soren Brinkmann Signed-off-by: Rob Herring diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt index a186181..51b943c 100644 --- a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt @@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -TZ1090-PDC's pin configuration nodes act as a container for an abitrary number +TZ1090-PDC's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt index 4b27c99..49d0e60 100644 --- a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt @@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -TZ1090's pin configuration nodes act as a container for an abitrary number of +TZ1090's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/lantiq,falcon-pinumx.txt b/Documentation/devicetree/bindings/pinctrl/lantiq,falcon-pinumx.txt index daa7689..ac4da9f 100644 --- a/Documentation/devicetree/bindings/pinctrl/lantiq,falcon-pinumx.txt +++ b/Documentation/devicetree/bindings/pinctrl/lantiq,falcon-pinumx.txt @@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Lantiq's pin configuration nodes act as a container for an abitrary number of +Lantiq's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those group(s), and two pin configuration parameters: diff --git a/Documentation/devicetree/bindings/pinctrl/lantiq,xway-pinumx.txt b/Documentation/devicetree/bindings/pinctrl/lantiq,xway-pinumx.txt index b5469db..e89b467 100644 --- a/Documentation/devicetree/bindings/pinctrl/lantiq,xway-pinumx.txt +++ b/Documentation/devicetree/bindings/pinctrl/lantiq,xway-pinumx.txt @@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Lantiq's pin configuration nodes act as a container for an abitrary number of +Lantiq's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those group(s), and two pin configuration parameters: diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt index 61e73cd..3c8ce28 100644 --- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt @@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Tegra's pin configuration nodes act as a container for an abitrary number of +Tegra's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt index c596a6a..5f55be5 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt @@ -13,7 +13,7 @@ Optional properties: Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices. -SiRFprimaII's pinmux nodes act as a container for an abitrary number of subnodes. +SiRFprimaII's pinmux nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a group of pins. Required subnode-properties: diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl_spear.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl_spear.txt index b4480d5..4586155 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl_spear.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl_spear.txt @@ -32,7 +32,7 @@ Required properties: Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices. -SPEAr's pinmux nodes act as a container for an abitrary number of subnodes. Each +SPEAr's pinmux nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents muxing for a pin, a group, or a list of pins or groups. diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt index 2fb90b3..a7bde64 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt @@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Qualcomm's pin configuration nodes act as a container for an abitrary number of +Qualcomm's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt index ffafa19..c4ea61a 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt @@ -47,7 +47,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -The pin configuration nodes act as a container for an abitrary number of +The pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt index e33e4dc..6e88e91 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt @@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Qualcomm's pin configuration nodes act as a container for an abitrary number of +Qualcomm's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.txt index 93b7de9..eb8d8aa 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.txt @@ -47,7 +47,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -The pin configuration nodes act as a container for an abitrary number of +The pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.txt index d2ea80d..e4d6a9d 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.txt @@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices, including the meaning of the phrase "pin configuration node". -Qualcomm's pin configuration nodes act as a container for an abitrary number of +Qualcomm's pin configuration nodes act as a container for an arbitrary number of subnodes. Each of these subnodes represents some desired configuration for a pin, a group, or a list of pins or groups. This configuration can include the mux function to select on those pin(s)/group(s), and various pin configuration -- cgit v0.10.2 From 50212b425d0ef8b606b316826b56abbb48680758 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 18 Nov 2014 13:16:13 +0100 Subject: can: gs_usb: add .ndo_change_mtu function Use common can_change_mtu function. Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 04b0f84..009acc8 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -718,6 +718,7 @@ static const struct net_device_ops gs_usb_netdev_ops = { .ndo_open = gs_can_open, .ndo_stop = gs_can_close, .ndo_start_xmit = gs_can_start_xmit, + .ndo_change_mtu = can_change_mtu, }; static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf) -- cgit v0.10.2 From d6fdb38b4f2c4090e176aa55fe73dd2f45cfa4a6 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 29 Oct 2014 18:45:23 +0800 Subject: can: m_can: add .ndo_change_mtu function Use common can_change_mtu function. Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 10d571e..e61886b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -992,6 +992,7 @@ static const struct net_device_ops m_can_netdev_ops = { .ndo_open = m_can_open, .ndo_stop = m_can_close, .ndo_start_xmit = m_can_start_xmit, + .ndo_change_mtu = can_change_mtu, }; static int register_m_can_dev(struct net_device *dev) -- cgit v0.10.2 From efe22286e05751e9913a8002fefdb45cdb7361ad Mon Sep 17 00:00:00 2001 From: David Cohen Date: Wed, 15 Oct 2014 14:41:50 -0700 Subject: can: m_can: add CONFIG_HAS_IOMEM dependence m_can uses io memory which makes it not compilable on architectures without HAS_IOMEM such as UML: drivers/built-in.o: In function `m_can_plat_probe': m_can.c:(.text+0x218cc5): undefined reference to `devm_ioremap_resource' m_can.c:(.text+0x218df9): undefined reference to `devm_ioremap' Signed-off-by: David Cohen Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/Kconfig b/drivers/net/can/m_can/Kconfig index fca5482..04f20dd 100644 --- a/drivers/net/can/m_can/Kconfig +++ b/drivers/net/can/m_can/Kconfig @@ -1,4 +1,5 @@ config CAN_M_CAN + depends on HAS_IOMEM tristate "Bosch M_CAN devices" ---help--- Say Y here if you want to support for Bosch M_CAN controller. -- cgit v0.10.2 From 962845da54afe2fc7ebf64c2d8bb5aa65a826b2f Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 7 Nov 2014 16:45:14 +0800 Subject: can: m_can: add missing message RAM initialization The M_CAN message RAM is usually equipped with a parity or ECC functionality. But RAM cells suffer a hardware reset and can therefore hold arbitrary content at startup - including parity and/or ECC bits. To prevent the M_CAN controller detecting checksum errors when reading potentially uninitialized TX message RAM content to transmit CAN frames the TX message RAM has to be written with (any kind of) initial data. Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index e61886b..268ad50 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1010,7 +1010,7 @@ static int m_can_of_parse_mram(struct platform_device *pdev, struct resource *res; void __iomem *addr; u32 out_val[MRAM_CFG_LEN]; - int ret; + int i, start, end, ret; /* message ram could be shared */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); @@ -1061,6 +1061,15 @@ static int m_can_of_parse_mram(struct platform_device *pdev, priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num, priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num); + /* initialize the entire Message RAM in use to avoid possible + * ECC/parity checksum errors when reading an uninitialized buffer + */ + start = priv->mcfg[MRAM_SIDF].off; + end = priv->mcfg[MRAM_TXB].off + + priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE; + for (i = start; i < end; i += 4) + writel(0x0, priv->mram_base + i); + return 0; } -- cgit v0.10.2 From f6a99649526370c7a88b86736c02f2a74b5b20e7 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 29 Oct 2014 18:45:21 +0800 Subject: can: m_can: fix possible sleep in napi poll The m_can_get_berr_counter function can sleep and it may be called in napi poll function. Rework it to fix the following warning. root@imx6qdlsolo:~# cangen can0 -f -L 12 -D 112233445566778899001122 [ 1846.017565] m_can 20e8000.can can0: entered error warning state [ 1846.023551] ------------[ cut here ]------------ [ 1846.028216] WARNING: CPU: 0 PID: 560 at kernel/locking/mutex.c:867 mutex_trylock+0x218/0x23c() [ 1846.036889] DEBUG_LOCKS_WARN_ON(in_interrupt()) [ 1846.041263] Modules linked in: [ 1846.044594] CPU: 0 PID: 560 Comm: cangen Not tainted 3.17.0-rc4-next-20140915-00010-g032d018-dirty #477 [ 1846.054033] Backtrace: [ 1846.056557] [<80012448>] (dump_backtrace) from [<80012728>] (show_stack+0x18/0x1c) [ 1846.064180] r6:809a07ec r5:809a07ec r4:00000000 r3:00000000 [ 1846.069966] [<80012710>] (show_stack) from [<806c9ee0>] (dump_stack+0x8c/0xa4) [ 1846.077264] [<806c9e54>] (dump_stack) from [<8002aa78>] (warn_slowpath_common+0x70/0x94) [ 1846.085403] r6:806cd1b0 r5:00000009 r4:be1d5c20 r3:be07b0c0 [ 1846.091204] [<8002aa08>] (warn_slowpath_common) from [<8002aad4>] (warn_slowpath_fmt+0x38/0x40) [ 1846.099951] r8:8119106c r7:80515aa4 r6:be027000 r5:00000001 r4:809d1df4 [ 1846.106830] [<8002aaa0>] (warn_slowpath_fmt) from [<806cd1b0>] (mutex_trylock+0x218/0x23c) [ 1846.115141] r3:80851c88 r2:8084fb74 [ 1846.118804] [<806ccf98>] (mutex_trylock) from [<80515aa4>] (clk_prepare_lock+0x14/0xf4) [ 1846.126859] r8:00000040 r7:be1d5cec r6:be027000 r5:be255800 r4:be027000 [ 1846.133737] [<80515a90>] (clk_prepare_lock) from [<80517660>] (clk_prepare+0x14/0x2c) [ 1846.141583] r5:be255800 r4:be027000 [ 1846.145272] [<8051764c>] (clk_prepare) from [<8041ff14>] (m_can_get_berr_counter+0x20/0xd4) [ 1846.153672] r4:be255800 r3:be07b0c0 [ 1846.157325] [<8041fef4>] (m_can_get_berr_counter) from [<80420428>] (m_can_poll+0x310/0x8fc) [ 1846.165809] r7:bd4dc540 r6:00000744 r5:11300000 r4:be255800 [ 1846.171590] [<80420118>] (m_can_poll) from [<8056a468>] (net_rx_action+0xcc/0x1b4) [ 1846.179204] r10:00000101 r9:be255ebc r8:00000040 r7:be7c3208 r6:8097c100 r5:be7c3200 [ 1846.187192] r4:0000012c [ 1846.189779] [<8056a39c>] (net_rx_action) from [<8002deec>] (__do_softirq+0xfc/0x2c4) [ 1846.197568] r10:00000101 r9:8097c088 r8:00000003 r7:8097c080 r6:40000001 r5:8097c08c [ 1846.205559] r4:00000020 [ 1846.208144] [<8002ddf0>] (__do_softirq) from [<8002e194>] (do_softirq+0x7c/0x88) [ 1846.215588] r10:00000000 r9:bd516a60 r8:be18ce00 r7:00000000 r6:be255800 r5:8056c0ec [ 1846.223578] r4:60000093 [ 1846.226163] [<8002e118>] (do_softirq) from [<8002e288>] (__local_bh_enable_ip+0xe8/0x10c) [ 1846.234386] r4:00000200 r3:be1d4000 [ 1846.238036] [<8002e1a0>] (__local_bh_enable_ip) from [<8056c108>] (__dev_queue_xmit+0x314/0x6b0) [ 1846.246868] r6:be255800 r5:bd516a00 r4:00000000 r3:be07b0c0 [ 1846.252645] [<8056bdf4>] (__dev_queue_xmit) from [<8056c4b8>] (dev_queue_xmit+0x14/0x18) Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 268ad50..214160b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -481,11 +481,23 @@ static int m_can_handle_lec_err(struct net_device *dev, return 1; } +static int __m_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct m_can_priv *priv = netdev_priv(dev); + unsigned int ecr; + + ecr = m_can_read(priv, M_CAN_ECR); + bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; + bec->txerr = ecr & ECR_TEC_MASK; + + return 0; +} + static int m_can_get_berr_counter(const struct net_device *dev, struct can_berr_counter *bec) { struct m_can_priv *priv = netdev_priv(dev); - unsigned int ecr; int err; err = clk_prepare_enable(priv->hclk); @@ -498,9 +510,7 @@ static int m_can_get_berr_counter(const struct net_device *dev, return err; } - ecr = m_can_read(priv, M_CAN_ECR); - bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; - bec->txerr = ecr & ECR_TEC_MASK; + __m_can_get_berr_counter(dev, bec); clk_disable_unprepare(priv->cclk); clk_disable_unprepare(priv->hclk); @@ -544,7 +554,7 @@ static int m_can_handle_state_change(struct net_device *dev, if (unlikely(!skb)) return 0; - m_can_get_berr_counter(dev, &bec); + __m_can_get_berr_counter(dev, &bec); switch (new_state) { case CAN_STATE_ERROR_ACTIVE: -- cgit v0.10.2 From 921f168109d029e3b59459c3a2754f0e2a70fad3 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 18 Nov 2014 19:00:54 +0800 Subject: can: m_can: fix not set can_dlc for remote frame The original code missed to set the cf->can_dlc in the RTR case, so add it. Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 214160b..98f7e0e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -330,7 +330,7 @@ static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf, u32 rxfs) { struct m_can_priv *priv = netdev_priv(dev); - u32 id, fgi; + u32 id, fgi, dlc; /* calculate the fifo get index for where to read data */ fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF; @@ -340,11 +340,12 @@ static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf, else cf->can_id = (id >> 18) & CAN_SFF_MASK; + dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); + cf->can_dlc = get_can_dlc((dlc >> 16) & 0x0F); + if (id & RX_BUF_RTR) { cf->can_id |= CAN_RTR_FLAG; } else { - id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); - cf->can_dlc = get_can_dlc((id >> 16) & 0x0F); *(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DATA(0)); *(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi, -- cgit v0.10.2 From 7660f633070986ab4ee41c063a90e140d5781e13 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 29 Oct 2014 18:45:24 +0800 Subject: can: m_can: add missing delay after setting CCCR_INIT bit The spec mentions there may be a delay until the value written to INIT can be read back due to the synchronization mechanism between the two clock domains. But it does not indicate the exact clock cycles needed. The 5us delay is a test value and seems ok. Without the delay, CCCR.CCE bit may fail to be set and then the initialization fail sometimes when do repeatly up and down. Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 98f7e0e..3ad7d88 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -296,6 +296,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv, if (enable) { /* enable m_can configuration */ m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT); + udelay(5); /* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */ m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE); } else { -- cgit v0.10.2 From a93f5cae67b366e4ce7e9eb336e2885309fd6612 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 29 Oct 2014 18:45:22 +0800 Subject: can: m_can: fix incorrect error messages Fix a few error messages. Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 3ad7d88..2f61676 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -608,14 +608,14 @@ static int m_can_handle_state_errors(struct net_device *dev, u32 psr) if ((psr & PSR_EP) && (priv->can.state != CAN_STATE_ERROR_PASSIVE)) { - netdev_dbg(dev, "entered error warning state\n"); + netdev_dbg(dev, "entered error passive state\n"); work_done += m_can_handle_state_change(dev, CAN_STATE_ERROR_PASSIVE); } if ((psr & PSR_BO) && (priv->can.state != CAN_STATE_BUS_OFF)) { - netdev_dbg(dev, "entered error warning state\n"); + netdev_dbg(dev, "entered error bus off state\n"); work_done += m_can_handle_state_change(dev, CAN_STATE_BUS_OFF); } @@ -627,7 +627,7 @@ static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus) { if (irqstatus & IR_WDI) netdev_err(dev, "Message RAM Watchdog event due to missing READY\n"); - if (irqstatus & IR_BEU) + if (irqstatus & IR_ELO) netdev_err(dev, "Error Logging Overflow\n"); if (irqstatus & IR_BEU) netdev_err(dev, "Bit Error Uncorrected\n"); -- cgit v0.10.2 From 80646733f11c2e9de3b6339f7e635047e6087280 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 18 Nov 2014 19:00:55 +0800 Subject: can: m_can: update to support CAN FD features Bosch M_CAN is CAN FD capable device. This patch implements the CAN FD features include up to 64 bytes payload and bitrate switch function. 1) Change the Rx FIFO and Tx Buffer to 64 bytes for support CAN FD up to 64 bytes payload. It's backward compatible with old 8 bytes normal CAN frame. 2) Allocate can frame or canfd frame based on EDL bit 3) Bitrate Switch function is disabled by default and will be enabled according to CANFD_BRS bit in cf->flags. Acked-by: Oliver Hartkopp Signed-off-by: Dong Aisheng Signed-off-by: Marc Kleine-Budde diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 2f61676..d7bc462 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -105,14 +105,36 @@ enum m_can_mram_cfg { MRAM_CFG_NUM, }; +/* Fast Bit Timing & Prescaler Register (FBTP) */ +#define FBTR_FBRP_MASK 0x1f +#define FBTR_FBRP_SHIFT 16 +#define FBTR_FTSEG1_SHIFT 8 +#define FBTR_FTSEG1_MASK (0xf << FBTR_FTSEG1_SHIFT) +#define FBTR_FTSEG2_SHIFT 4 +#define FBTR_FTSEG2_MASK (0x7 << FBTR_FTSEG2_SHIFT) +#define FBTR_FSJW_SHIFT 0 +#define FBTR_FSJW_MASK 0x3 + /* Test Register (TEST) */ #define TEST_LBCK BIT(4) /* CC Control Register(CCCR) */ -#define CCCR_TEST BIT(7) -#define CCCR_MON BIT(5) -#define CCCR_CCE BIT(1) -#define CCCR_INIT BIT(0) +#define CCCR_TEST BIT(7) +#define CCCR_CMR_MASK 0x3 +#define CCCR_CMR_SHIFT 10 +#define CCCR_CMR_CANFD 0x1 +#define CCCR_CMR_CANFD_BRS 0x2 +#define CCCR_CMR_CAN 0x3 +#define CCCR_CME_MASK 0x3 +#define CCCR_CME_SHIFT 8 +#define CCCR_CME_CAN 0 +#define CCCR_CME_CANFD 0x1 +#define CCCR_CME_CANFD_BRS 0x2 +#define CCCR_TEST BIT(7) +#define CCCR_MON BIT(5) +#define CCCR_CCE BIT(1) +#define CCCR_INIT BIT(0) +#define CCCR_CANFD 0x10 /* Bit Timing & Prescaler Register (BTP) */ #define BTR_BRP_MASK 0x3ff @@ -204,6 +226,7 @@ enum m_can_mram_cfg { /* Rx Buffer / FIFO Element Size Configuration (RXESC) */ #define M_CAN_RXESC_8BYTES 0x0 +#define M_CAN_RXESC_64BYTES 0x777 /* Tx Buffer Configuration(TXBC) */ #define TXBC_NDTB_OFF 16 @@ -211,6 +234,7 @@ enum m_can_mram_cfg { /* Tx Buffer Element Size Configuration(TXESC) */ #define TXESC_TBDS_8BYTES 0x0 +#define TXESC_TBDS_64BYTES 0x7 /* Tx Event FIFO Con.guration (TXEFC) */ #define TXEFC_EFS_OFF 16 @@ -219,11 +243,11 @@ enum m_can_mram_cfg { /* Message RAM Configuration (in bytes) */ #define SIDF_ELEMENT_SIZE 4 #define XIDF_ELEMENT_SIZE 8 -#define RXF0_ELEMENT_SIZE 16 -#define RXF1_ELEMENT_SIZE 16 +#define RXF0_ELEMENT_SIZE 72 +#define RXF1_ELEMENT_SIZE 72 #define RXB_ELEMENT_SIZE 16 #define TXE_ELEMENT_SIZE 8 -#define TXB_ELEMENT_SIZE 16 +#define TXB_ELEMENT_SIZE 72 /* Message RAM Elements */ #define M_CAN_FIFO_ID 0x0 @@ -231,11 +255,17 @@ enum m_can_mram_cfg { #define M_CAN_FIFO_DATA(n) (0x8 + ((n) << 2)) /* Rx Buffer Element */ +/* R0 */ #define RX_BUF_ESI BIT(31) #define RX_BUF_XTD BIT(30) #define RX_BUF_RTR BIT(29) +/* R1 */ +#define RX_BUF_ANMF BIT(31) +#define RX_BUF_EDL BIT(21) +#define RX_BUF_BRS BIT(20) /* Tx Buffer Element */ +/* R0 */ #define TX_BUF_XTD BIT(30) #define TX_BUF_RTR BIT(29) @@ -327,42 +357,67 @@ static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv) m_can_write(priv, M_CAN_ILE, 0x0); } -static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf, - u32 rxfs) +static void m_can_read_fifo(struct net_device *dev, u32 rxfs) { + struct net_device_stats *stats = &dev->stats; struct m_can_priv *priv = netdev_priv(dev); + struct canfd_frame *cf; + struct sk_buff *skb; u32 id, fgi, dlc; + int i; /* calculate the fifo get index for where to read data */ fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF; + dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); + if (dlc & RX_BUF_EDL) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + if (dlc & RX_BUF_EDL) + cf->len = can_dlc2len((dlc >> 16) & 0x0F); + else + cf->len = get_can_dlc((dlc >> 16) & 0x0F); + id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_ID); if (id & RX_BUF_XTD) cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG; else cf->can_id = (id >> 18) & CAN_SFF_MASK; - dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); - cf->can_dlc = get_can_dlc((dlc >> 16) & 0x0F); + if (id & RX_BUF_ESI) { + cf->flags |= CANFD_ESI; + netdev_dbg(dev, "ESI Error\n"); + } - if (id & RX_BUF_RTR) { + if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) { cf->can_id |= CAN_RTR_FLAG; } else { - *(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi, - M_CAN_FIFO_DATA(0)); - *(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi, - M_CAN_FIFO_DATA(1)); + if (dlc & RX_BUF_BRS) + cf->flags |= CANFD_BRS; + + for (i = 0; i < cf->len; i += 4) + *(u32 *)(cf->data + i) = + m_can_fifo_read(priv, fgi, + M_CAN_FIFO_DATA(i / 4)); } /* acknowledge rx fifo 0 */ m_can_write(priv, M_CAN_RXF0A, fgi); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + + netif_receive_skb(skb); } static int m_can_do_rx_poll(struct net_device *dev, int quota) { struct m_can_priv *priv = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; - struct sk_buff *skb; - struct can_frame *frame; u32 pkts = 0; u32 rxfs; @@ -376,18 +431,7 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) if (rxfs & RXFS_RFL) netdev_warn(dev, "Rx FIFO 0 Message Lost\n"); - skb = alloc_can_skb(dev, &frame); - if (!skb) { - stats->rx_dropped++; - return pkts; - } - - m_can_read_fifo(dev, frame, rxfs); - - stats->rx_packets++; - stats->rx_bytes += frame->can_dlc; - - netif_receive_skb(skb); + m_can_read_fifo(dev, rxfs); quota--; pkts++; @@ -745,10 +789,23 @@ static const struct can_bittiming_const m_can_bittiming_const = { .brp_inc = 1, }; +static const struct can_bittiming_const m_can_data_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + static int m_can_set_bittiming(struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; u16 brp, sjw, tseg1, tseg2; u32 reg_btp; @@ -759,7 +816,17 @@ static int m_can_set_bittiming(struct net_device *dev) reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) | (tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT); m_can_write(priv, M_CAN_BTP, reg_btp); - netdev_dbg(dev, "setting BTP 0x%x\n", reg_btp); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + brp = dbt->brp - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) | + (tseg1 << FBTR_FTSEG1_SHIFT) | + (tseg2 << FBTR_FTSEG2_SHIFT); + m_can_write(priv, M_CAN_FBTP, reg_btp); + } return 0; } @@ -779,8 +846,8 @@ static void m_can_chip_config(struct net_device *dev) m_can_config_endisable(priv, true); - /* RX Buffer/FIFO Element Size 8 bytes data field */ - m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_8BYTES); + /* RX Buffer/FIFO Element Size 64 bytes data field */ + m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_64BYTES); /* Accept Non-matching Frames Into FIFO 0 */ m_can_write(priv, M_CAN_GFC, 0x0); @@ -789,8 +856,8 @@ static void m_can_chip_config(struct net_device *dev) m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) | priv->mcfg[MRAM_TXB].off); - /* only support 8 bytes firstly */ - m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_8BYTES); + /* support 64 bytes payload */ + m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) | priv->mcfg[MRAM_TXE].off); @@ -805,7 +872,8 @@ static void m_can_chip_config(struct net_device *dev) RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off); cccr = m_can_read(priv, M_CAN_CCCR); - cccr &= ~(CCCR_TEST | CCCR_MON); + cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | + (CCCR_CME_MASK << CCCR_CME_SHIFT)); test = m_can_read(priv, M_CAN_TEST); test &= ~TEST_LBCK; @@ -817,6 +885,9 @@ static void m_can_chip_config(struct net_device *dev) test |= TEST_LBCK; } + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + m_can_write(priv, M_CAN_CCCR, cccr); m_can_write(priv, M_CAN_TEST, test); @@ -881,11 +952,13 @@ static struct net_device *alloc_m_can_dev(void) priv->dev = dev; priv->can.bittiming_const = &m_can_bittiming_const; + priv->can.data_bittiming_const = &m_can_data_bittiming_const; priv->can.do_set_mode = m_can_set_mode; priv->can.do_get_berr_counter = m_can_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | - CAN_CTRLMODE_BERR_REPORTING; + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_FD; return dev; } @@ -968,8 +1041,9 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); - struct can_frame *cf = (struct can_frame *)skb->data; - u32 id; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 id, cccr; + int i; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; @@ -988,11 +1062,28 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, /* message ram configuration */ m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); - m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, cf->can_dlc << 16); - m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(0), *(u32 *)(cf->data + 0)); - m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(1), *(u32 *)(cf->data + 4)); + m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16); + + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + can_put_echo_skb(skb, dev, 0); + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + cccr = m_can_read(priv, M_CAN_CCCR); + cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + if (can_is_canfd_skb(skb)) { + if (cf->flags & CANFD_BRS) + cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT; + else + cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT; + } else { + cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + } + m_can_write(priv, M_CAN_CCCR, cccr); + } + /* enable first TX buffer to start transfer */ m_can_write(priv, M_CAN_TXBTIE, 0x1); m_can_write(priv, M_CAN_TXBAR, 0x1); -- cgit v0.10.2 From 11bf7828a59880427403e13dcff8228d67e9e0f7 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Mon, 17 Nov 2014 16:24:54 -0800 Subject: vxlan: Inline vxlan_gso_check(). Suggested-by: Or Gerlitz Signed-off-by: Joe Stringer Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 6b65863..e1e335c 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -67,12 +67,6 @@ #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ -/* VXLAN protocol header */ -struct vxlanhdr { - __be32 vx_flags; - __be32 vx_vni; -}; - /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 * for compatibility with early adopters. @@ -1571,19 +1565,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -bool vxlan_gso_check(struct sk_buff *skb) -{ - if ((skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) && - (skb->inner_protocol_type != ENCAP_TYPE_ETHER || - skb->inner_protocol != htons(ETH_P_TEB) || - (skb_inner_mac_header(skb) - skb_transport_header(skb) != - sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) - return false; - - return true; -} -EXPORT_SYMBOL_GPL(vxlan_gso_check); - #if IS_ENABLED(CONFIG_IPV6) static int vxlan6_xmit_skb(struct vxlan_sock *vs, struct dst_entry *dst, struct sk_buff *skb, diff --git a/include/net/vxlan.h b/include/net/vxlan.h index afadf8e..57cccd0 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -8,6 +8,12 @@ #define VNI_HASH_BITS 10 #define VNI_HASH_SIZE (1<gso_type & SKB_GSO_UDP_TUNNEL) && + (skb->inner_protocol_type != ENCAP_TYPE_ETHER || + skb->inner_protocol != htons(ETH_P_TEB) || + (skb_inner_mac_header(skb) - skb_transport_header(skb) != + sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) + return false; + + return true; +} /* IP header + UDP + VXLAN + Ethernet header */ #define VXLAN_HEADROOM (20 + 8 + 8 + 14) -- cgit v0.10.2 From 280ba51d60be6f4ca3347eaa60783314f38df72e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 18 Nov 2014 22:35:31 +0100 Subject: mac80211: minstrel_ht: fix a crash in rate sorting The commit 5935839ad73583781b8bbe8d91412f6826e218a4 "mac80211: improve minstrel_ht rate sorting by throughput & probability" introduced a crash on rate sorting that occurs when the rate added to the sorting array is faster than all the previous rates. Due to an off-by-one error, it reads the rate index from tp_list[-1], which contains uninitialized stack garbage, and then uses the resulting index for accessing the group rate stats, leading to a crash if the garbage value is big enough. Cc: Thomas Huehn Reported-by: Jouni Malinen Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index df90ce2..408fd8ab 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -252,19 +252,16 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; cur_prob = mi->groups[cur_group].rates[cur_idx].probability; - tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; - tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; - tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; - - while (j > 0 && (cur_thr > tmp_thr || - (cur_thr == tmp_thr && cur_prob > tmp_prob))) { - j--; + do { tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; - } + if (cur_thr < tmp_thr || + (cur_thr == tmp_thr && cur_prob <= tmp_prob)) + break; + j--; + } while (j > 0); if (j < MAX_THR_RATES - 1) { memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) * -- cgit v0.10.2 From 0526f276f94758245ac5886604fe8c805c1b6d2c Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Wed, 19 Nov 2014 16:52:15 +0900 Subject: ARM: dts: Explicitly set dr_mode on exynos5250-snow Explicitly set the dr_mode for the dwc3 controller on the Snow board to host mode. This is required to ensure the controller is initialized in the right mode if the kernel is build with USB gadget support. Signed-off-by: Sjoerd Simons Reviewed-by: Javier Martinez Canillas Signed-off-by: Kukjin Kim diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts index e51fcef..60429ad 100644 --- a/arch/arm/boot/dts/exynos5250-snow.dts +++ b/arch/arm/boot/dts/exynos5250-snow.dts @@ -624,4 +624,8 @@ num-cs = <1>; }; +&usbdrd_dwc3 { + dr_mode = "host"; +}; + #include "cros-ec-keyboard.dtsi" diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index f21b9aa..d55c1a2 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -555,7 +555,7 @@ #size-cells = <1>; ranges; - dwc3 { + usbdrd_dwc3: dwc3 { compatible = "synopsys,dwc3"; reg = <0x12000000 0x10000>; interrupts = <0 72 0>; -- cgit v0.10.2 From 204ec6e07ea7aff863df0f7c53301f9cbbfbb9d3 Mon Sep 17 00:00:00 2001 From: Troy Clark Date: Mon, 17 Nov 2014 14:33:17 -0800 Subject: usb: serial: ftdi_sio: add PIDs for Matrix Orbital products Add PIDs for new Matrix Orbital GTT series products. Signed-off-by: Troy Clark Cc: stable [johan: shorten commit message ] Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0dad8ce..1ebb351 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -470,6 +470,39 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 6786b70..e52409c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -926,8 +926,8 @@ #define BAYER_CONTOUR_CABLE_PID 0x6001 /* - * The following are the values for the Matrix Orbital FTDI Range - * Anything in this range will use an FT232RL. + * Matrix Orbital Intelligent USB displays. + * http://www.matrixorbital.com */ #define MTXORB_VID 0x1B3D #define MTXORB_FTDI_RANGE_0100_PID 0x0100 @@ -1186,8 +1186,39 @@ #define MTXORB_FTDI_RANGE_01FD_PID 0x01FD #define MTXORB_FTDI_RANGE_01FE_PID 0x01FE #define MTXORB_FTDI_RANGE_01FF_PID 0x01FF - - +#define MTXORB_FTDI_RANGE_4701_PID 0x4701 +#define MTXORB_FTDI_RANGE_9300_PID 0x9300 +#define MTXORB_FTDI_RANGE_9301_PID 0x9301 +#define MTXORB_FTDI_RANGE_9302_PID 0x9302 +#define MTXORB_FTDI_RANGE_9303_PID 0x9303 +#define MTXORB_FTDI_RANGE_9304_PID 0x9304 +#define MTXORB_FTDI_RANGE_9305_PID 0x9305 +#define MTXORB_FTDI_RANGE_9306_PID 0x9306 +#define MTXORB_FTDI_RANGE_9307_PID 0x9307 +#define MTXORB_FTDI_RANGE_9308_PID 0x9308 +#define MTXORB_FTDI_RANGE_9309_PID 0x9309 +#define MTXORB_FTDI_RANGE_930A_PID 0x930A +#define MTXORB_FTDI_RANGE_930B_PID 0x930B +#define MTXORB_FTDI_RANGE_930C_PID 0x930C +#define MTXORB_FTDI_RANGE_930D_PID 0x930D +#define MTXORB_FTDI_RANGE_930E_PID 0x930E +#define MTXORB_FTDI_RANGE_930F_PID 0x930F +#define MTXORB_FTDI_RANGE_9310_PID 0x9310 +#define MTXORB_FTDI_RANGE_9311_PID 0x9311 +#define MTXORB_FTDI_RANGE_9312_PID 0x9312 +#define MTXORB_FTDI_RANGE_9313_PID 0x9313 +#define MTXORB_FTDI_RANGE_9314_PID 0x9314 +#define MTXORB_FTDI_RANGE_9315_PID 0x9315 +#define MTXORB_FTDI_RANGE_9316_PID 0x9316 +#define MTXORB_FTDI_RANGE_9317_PID 0x9317 +#define MTXORB_FTDI_RANGE_9318_PID 0x9318 +#define MTXORB_FTDI_RANGE_9319_PID 0x9319 +#define MTXORB_FTDI_RANGE_931A_PID 0x931A +#define MTXORB_FTDI_RANGE_931B_PID 0x931B +#define MTXORB_FTDI_RANGE_931C_PID 0x931C +#define MTXORB_FTDI_RANGE_931D_PID 0x931D +#define MTXORB_FTDI_RANGE_931E_PID 0x931E +#define MTXORB_FTDI_RANGE_931F_PID 0x931F /* * The Mobility Lab (TML) -- cgit v0.10.2 From 6bab4a8a1888729f17f4923cc5867e4674f66333 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 18 Nov 2014 23:59:33 +0100 Subject: clockevent: sun4i: Fix race condition in the probe code The interrupts were activated and the handler registered before the clockevent was registered in the probe function. The interrupt handler, however, was making the assumption that the clockevent device was registered. That could cause a null pointer dereference if the timer interrupt was firing during this narrow window. Fix that by moving the clockevent registration before the interrupt is enabled. Reported-by: Roman Byshko Signed-off-by: Maxime Ripard Cc: stable@vger.kernel.org Signed-off-by: Daniel Lezcano diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index efb17c3..f4a9c00 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -182,6 +182,12 @@ static void __init sun4i_timer_init(struct device_node *node) /* Make sure timer is stopped before playing with interrupts */ sun4i_clkevt_time_stop(0); + sun4i_clockevent.cpumask = cpu_possible_mask; + sun4i_clockevent.irq = irq; + + clockevents_config_and_register(&sun4i_clockevent, rate, + TIMER_SYNC_TICKS, 0xffffffff); + ret = setup_irq(irq, &sun4i_timer_irq); if (ret) pr_warn("failed to setup irq %d\n", irq); @@ -189,12 +195,6 @@ static void __init sun4i_timer_init(struct device_node *node) /* Enable timer0 interrupt */ val = readl(timer_base + TIMER_IRQ_EN_REG); writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); - - sun4i_clockevent.cpumask = cpu_possible_mask; - sun4i_clockevent.irq = irq; - - clockevents_config_and_register(&sun4i_clockevent, rate, - TIMER_SYNC_TICKS, 0xffffffff); } CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", sun4i_timer_init); -- cgit v0.10.2 From 5d1678a33c731b56e245e888fdae5e88efce0997 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 18 Nov 2014 11:25:19 +0100 Subject: USB: keyspan: fix tty line-status reporting Fix handling of TTY error flags, which are not bitmasks and must specifically not be ORed together as this prevents the line discipline from recognising them. Also insert null characters when reporting overrun errors as these are not associated with the received character. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 93cb7ce..7799d8b 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -321,14 +321,19 @@ static void usa26_indat_callback(struct urb *urb) /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } @@ -649,14 +654,19 @@ static void usa49_indat_callback(struct urb *urb) } else { /* some bytes had errors, every byte has status */ for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } @@ -713,15 +723,19 @@ static void usa49wg_indat_callback(struct urb *urb) */ for (x = 0; x + 1 < len && i + 1 < urb->actual_length; x += 2) { - int stat = data[i], flag = 0; + int stat = data[i]; + int flag = TTY_NORMAL; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); i += 2; @@ -784,14 +798,20 @@ static void usa90_indat_callback(struct urb *urb) /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); for (i = 0; i + 1 < urb->actual_length; i += 2) { - int stat = data[i], flag = 0; - if (stat & RXERROR_OVERRUN) - flag |= TTY_OVERRUN; - if (stat & RXERROR_FRAMING) - flag |= TTY_FRAME; - if (stat & RXERROR_PARITY) - flag |= TTY_PARITY; + int stat = data[i]; + int flag = TTY_NORMAL; + + if (stat & RXERROR_OVERRUN) { + tty_insert_flip_char( + &port->port, 0, + TTY_OVERRUN); + } /* XXX should handle break (0x10) */ + if (stat & RXERROR_PARITY) + flag = TTY_PARITY; + else if (stat & RXERROR_FRAMING) + flag = TTY_FRAME; + tty_insert_flip_char(&port->port, data[i+1], flag); } -- cgit v0.10.2 From 855515a6d3731242d85850a206f2ec084c917338 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 18 Nov 2014 11:25:20 +0100 Subject: USB: keyspan: fix overrun-error reporting Fix reporting of overrun errors, which are not associated with a character. Instead insert a null character and report only once. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 7799d8b..077c714 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -311,12 +311,13 @@ static void usa26_indat_callback(struct urb *urb) if ((data[0] & 0x80) == 0) { /* no errors on individual bytes, only possible overrun err */ - if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else - err = 0; + if (data[0] & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } for (i = 1; i < urb->actual_length ; ++i) - tty_insert_flip_char(&port->port, data[i], err); + tty_insert_flip_char(&port->port, data[i], + TTY_NORMAL); } else { /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); @@ -787,13 +788,13 @@ static void usa90_indat_callback(struct urb *urb) if ((data[0] & 0x80) == 0) { /* no errors on individual bytes, only possible overrun err*/ - if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else - err = 0; + if (data[0] & RXERROR_OVERRUN) { + tty_insert_flip_char(&port->port, 0, + TTY_OVERRUN); + } for (i = 1; i < urb->actual_length ; ++i) tty_insert_flip_char(&port->port, - data[i], err); + data[i], TTY_NORMAL); } else { /* some bytes had errors, every byte has status */ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__); -- cgit v0.10.2 From 75bcbf29c284dd0154c3e895a0bd1ef0e796160e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 18 Nov 2014 11:25:21 +0100 Subject: USB: ssu100: fix overrun-error reporting Fix reporting of overrun errors, which should only be reported once using the inserted null character. Fixes: 6b8f1ca5581b ("USB: ssu100: set tty_flags in ssu100_process_packet") Cc: stable Signed-off-by: Johan Hovold diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index a7fe664..70a098d 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -490,10 +490,9 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr, if (*tty_flag == TTY_NORMAL) *tty_flag = TTY_FRAME; } - if (lsr & UART_LSR_OE){ + if (lsr & UART_LSR_OE) { port->icount.overrun++; - if (*tty_flag == TTY_NORMAL) - *tty_flag = TTY_OVERRUN; + tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); } } @@ -511,12 +510,8 @@ static void ssu100_process_read_urb(struct urb *urb) if ((len >= 4) && (packet[0] == 0x1b) && (packet[1] == 0x1b) && ((packet[2] == 0x00) || (packet[2] == 0x01))) { - if (packet[2] == 0x00) { + if (packet[2] == 0x00) ssu100_update_lsr(port, packet[3], &flag); - if (flag == TTY_OVERRUN) - tty_insert_flip_char(&port->port, 0, - TTY_OVERRUN); - } if (packet[2] == 0x01) ssu100_update_msr(port, packet[3]); -- cgit v0.10.2 From 6d0ba0432a5e10bc714ba9c5adc460e726e5fbb4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 8 Nov 2014 13:11:03 +0100 Subject: nfsd: correctly define v4.2 support attributes Even when security labels are disabled we support at least the same attributes as v4.1. Signed-off-by: Christoph Hellwig Cc: stable@kernel.org Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 747f3b95..33a46a8 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -335,12 +335,15 @@ void nfsd_lockd_shutdown(void); (NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT) #ifdef CONFIG_NFSD_V4_SECURITY_LABEL -#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ - (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL) +#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL #else -#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0 +#define NFSD4_2_SECURITY_ATTRS 0 #endif +#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ + (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \ + NFSD4_2_SECURITY_ATTRS) + static inline u32 nfsd_suppattrs0(u32 minorversion) { return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0 -- cgit v0.10.2 From 093a1468b6edb0e568be7311b8d2228d205702db Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 12 Nov 2014 18:04:04 -0500 Subject: SUNRPC: Fix locking around callback channel reply receive Both xprt_lookup_rqst() and xprt_complete_rqst() require that you take the transport lock in order to avoid races with xprt_transmit(). Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 3f959c6..f9c052d 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1019,17 +1019,12 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) xid = *p++; calldir = *p; - if (bc_xprt) - req = xprt_lookup_rqst(bc_xprt, xid); - - if (!req) { - printk(KERN_NOTICE - "%s: Got unrecognized reply: " - "calldir 0x%x xpt_bc_xprt %p xid %08x\n", - __func__, ntohl(calldir), - bc_xprt, ntohl(xid)); + if (!bc_xprt) return -EAGAIN; - } + spin_lock_bh(&bc_xprt->transport_lock); + req = xprt_lookup_rqst(bc_xprt, xid); + if (!req) + goto unlock_notfound; memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf)); /* @@ -1040,11 +1035,21 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) dst = &req->rq_private_buf.head[0]; src = &rqstp->rq_arg.head[0]; if (dst->iov_len < src->iov_len) - return -EAGAIN; /* whatever; just giving up. */ + goto unlock_eagain; /* whatever; just giving up. */ memcpy(dst->iov_base, src->iov_base, src->iov_len); xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len); rqstp->rq_arg.len = 0; + spin_unlock_bh(&bc_xprt->transport_lock); return 0; +unlock_notfound: + printk(KERN_NOTICE + "%s: Got unrecognized reply: " + "calldir 0x%x xpt_bc_xprt %p xid %08x\n", + __func__, ntohl(calldir), + bc_xprt, ntohl(xid)); +unlock_eagain: + spin_unlock_bh(&bc_xprt->transport_lock); + return -EAGAIN; } static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len) -- cgit v0.10.2 From 21255dad9dffa4407cab866f5561cb9568f7f7d8 Mon Sep 17 00:00:00 2001 From: James Cowgill Date: Thu, 13 Nov 2014 11:08:06 +0000 Subject: MIPS: Loongson3: Fix __node_distances undefined error export the __node_distances symbol in the loongson3 numa code to fix the build error: Building modules, stage 2. MODPOST 221 modules ERROR: "__node_distances" [drivers/block/nvme.ko] undefined! scripts/Makefile.modpost:90: recipe for target '__modpost' failed when building the kernel with: CONFIG_CPU_LOONGSON3=y CONFIG_NUMA=y CONFIG_BLK_DEV_NVME=m Signed-off-by: James Cowgill Cc: # v3.17+ Reviewed-by: James Hogan Reviewed-by: Huacai Chen Cc: linux-mips@linux-mips.org Cc: Wei Yongjun Patchwork: https://patchwork.linux-mips.org/patch/8444/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/loongson/loongson-3/numa.c b/arch/mips/loongson/loongson-3/numa.c index 37ed184..42323bc 100644 --- a/arch/mips/loongson/loongson-3/numa.c +++ b/arch/mips/loongson/loongson-3/numa.c @@ -33,6 +33,7 @@ static struct node_data prealloc__node_data[MAX_NUMNODES]; unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES]; +EXPORT_SYMBOL(__node_distances); struct node_data *__node_data[MAX_NUMNODES]; EXPORT_SYMBOL(__node_data); -- cgit v0.10.2 From 5829b0ecc584d15ae4eeabe69f2ab554bdec4689 Mon Sep 17 00:00:00 2001 From: James Cowgill Date: Thu, 13 Nov 2014 11:08:07 +0000 Subject: MIPS: IP27: Fix __node_distances undefined error export the __node_distances symbol in the ip27 memory code to fix the build error: Building modules, stage 2. MODPOST 311 modules ERROR: "__node_distances" [drivers/block/nvme.ko] undefined! scripts/Makefile.modpost:90: recipe for target '__modpost' failed when building the kernel with: CONFIG_SGI_IP27=y CONFIG_BLK_DEV_NVME=m Signed-off-by: James Cowgill Cc: # v3.15+ Reviewed-by: James Hogan Signed-off-by: Ralf Baechle diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index a95c00f..a304bcc 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -107,6 +107,7 @@ static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth) } unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES]; +EXPORT_SYMBOL(__node_distances); static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b) { -- cgit v0.10.2 From cc94ea31150f83d1f70ad854c920dd6b739c1628 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 4 Nov 2014 14:13:22 +0800 Subject: MIPS: Loongson: Fix the write-combine CCA value setting All Loongson-2/3 processors support _CACHE_UNCACHED_ACCELERATED, not only Loongson-3A. Signed-off-by: Huacai Chen Cc: John Crispin Cc: Steven J. Hill Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang Cc: Zhangjin Wu Patchwork: https://patchwork.linux-mips.org/patch/8319/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 94c4a0c..08dc945 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -765,7 +765,6 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) break; case PRID_REV_LOONGSON3A: c->cputype = CPU_LOONGSON3; - c->writecombine = _CACHE_UNCACHED_ACCELERATED; __cpu_name[cpu] = "ICT Loongson-3"; set_elf_platform(cpu, "loongson3a"); break; @@ -782,6 +781,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) MIPS_CPU_FPU | MIPS_CPU_LLSC | MIPS_CPU_32FPR; c->tlbsize = 64; + c->writecombine = _CACHE_UNCACHED_ACCELERATED; break; case PRID_IMP_LOONGSON_32: /* Loongson-1 */ decode_configs(c); -- cgit v0.10.2 From 7352c8b13dd9a848b0c5d6209d62761afb164dcb Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 4 Nov 2014 14:13:23 +0800 Subject: MIPS: Loongson: Set Loongson-3's ISA level to MIPS64R1 In CPU manual Loongson-3 is MIPS64R2 compatible, but during tests we found that its EI/DI instructions have problems. So we just set the ISA level to MIPS64R1. Signed-off-by: Huacai Chen Cc: John Crispin Cc: Steven J. Hill Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang Cc: Zhangjin Wu Patchwork: https://patchwork.linux-mips.org/patch/8320/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h index 7d28f95..6d69332 100644 --- a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h @@ -41,10 +41,8 @@ #define cpu_has_mcheck 0 #define cpu_has_mdmx 0 #define cpu_has_mips16 0 -#define cpu_has_mips32r1 0 #define cpu_has_mips32r2 0 #define cpu_has_mips3d 0 -#define cpu_has_mips64r1 0 #define cpu_has_mips64r2 0 #define cpu_has_mipsmt 0 #define cpu_has_prefetch 0 diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 08dc945..d5a4f38 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -757,26 +757,29 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->cputype = CPU_LOONGSON2; __cpu_name[cpu] = "ICT Loongson-2"; set_elf_platform(cpu, "loongson2e"); + set_isa(c, MIPS_CPU_ISA_III); break; case PRID_REV_LOONGSON2F: c->cputype = CPU_LOONGSON2; __cpu_name[cpu] = "ICT Loongson-2"; set_elf_platform(cpu, "loongson2f"); + set_isa(c, MIPS_CPU_ISA_III); break; case PRID_REV_LOONGSON3A: c->cputype = CPU_LOONGSON3; __cpu_name[cpu] = "ICT Loongson-3"; set_elf_platform(cpu, "loongson3a"); + set_isa(c, MIPS_CPU_ISA_M64R1); break; case PRID_REV_LOONGSON3B_R1: case PRID_REV_LOONGSON3B_R2: c->cputype = CPU_LOONGSON3; __cpu_name[cpu] = "ICT Loongson-3"; set_elf_platform(cpu, "loongson3b"); + set_isa(c, MIPS_CPU_ISA_M64R1); break; } - set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC | MIPS_CPU_32FPR; -- cgit v0.10.2 From bbaf113a481b6ce32444c125807ad3618643ce57 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Fri, 17 Oct 2014 18:10:24 +0300 Subject: MIPS: oprofile: Fix backtrace on 64-bit kernel Fix incorrect cast that always results in wrong address for the new frame on 64-bit kernels. Signed-off-by: Aaro Koskinen Cc: stable@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8110/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/oprofile/backtrace.c b/arch/mips/oprofile/backtrace.c index 6854ed5..83a1dfd 100644 --- a/arch/mips/oprofile/backtrace.c +++ b/arch/mips/oprofile/backtrace.c @@ -92,7 +92,7 @@ static inline int unwind_user_frame(struct stackframe *old_frame, /* This marks the end of the previous function, which means we overran. */ break; - stack_size = (unsigned) stack_adjustment; + stack_size = (unsigned long) stack_adjustment; } else if (is_ra_save_ins(&ip)) { int ra_slot = ip.i_format.simmediate; if (ra_slot < 0) -- cgit v0.10.2 From 58563817cfed0432e9a54476d5fc6c3aeba475e4 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 17 Nov 2014 09:30:23 +0000 Subject: MIPS: asm: uaccess: Add v1 register to clobber list on EVA When EVA is turned on and prefetching is being used in memcpy.S, the v1 register is being used as a helper register to the PREFE instruction. However, v1 ($3) was not in the clobber list, which means that the compiler did not preserve it across function calls, and that could corrupt the value of the register leading to all sorts of userland crashes. We fix this problem by using the DADDI_SCRATCH macro to define the clobbered register when CONFIG_EVA && CONFIG_CPU_HAS_PREFETCH are enabled. Signed-off-by: Markos Chandras Cc: # v3.15+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8510/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index a109510..b9ab717 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -773,10 +773,11 @@ extern void __put_user_unaligned_unknown(void); "jal\t" #destination "\n\t" #endif -#ifndef CONFIG_CPU_DADDI_WORKAROUNDS -#define DADDI_SCRATCH "$0" -#else +#if defined(CONFIG_CPU_DADDI_WORKAROUNDS) || (defined(CONFIG_EVA) && \ + defined(CONFIG_CPU_HAS_PREFETCH)) #define DADDI_SCRATCH "$3" +#else +#define DADDI_SCRATCH "$0" #endif extern size_t __copy_user(void *__to, const void *__from, size_t __n); -- cgit v0.10.2 From 6a8dff6ab16c903b0d8ef5fbf21543f39bf5d675 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 17 Nov 2014 09:31:07 +0000 Subject: MIPS: tlb-r4k: Add missing HTW stop/start sequences HTW needs to stop and start again whenever the EntryHI register changes otherwise an inflight HTW operation might use the new EntryHI register for updating an old entry and that could lead to crashes or even a machine check exception. We fix this by ensuring the HTW has stop whenever the EntryHI register is about to change Signed-off-by: Markos Chandras Cc: # v3.17+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8511/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c index fa6ebd4..c3917e2 100644 --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c @@ -299,6 +299,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) local_irq_save(flags); + htw_stop(); pid = read_c0_entryhi() & ASID_MASK; address &= (PAGE_MASK << 1); write_c0_entryhi(address | pid); @@ -346,6 +347,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) tlb_write_indexed(); } tlbw_use_hazard(); + htw_start(); flush_itlb_vm(vma); local_irq_restore(flags); } @@ -422,6 +424,7 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, local_irq_save(flags); /* Save old context and create impossible VPN2 value */ + htw_stop(); old_ctx = read_c0_entryhi(); old_pagemask = read_c0_pagemask(); wired = read_c0_wired(); @@ -443,6 +446,7 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, write_c0_entryhi(old_ctx); write_c0_pagemask(old_pagemask); + htw_start(); out: local_irq_restore(flags); return ret; -- cgit v0.10.2 From 51b1029d9966060c6ad02030e6f251425b4f06c1 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 17 Nov 2014 09:32:38 +0000 Subject: MIPS: lib: memcpy: Restore NOP on delay slot before returning to caller Commit cf62a8b8134dd3 ("MIPS: lib: memcpy: Use macro to build the copy_user code") switched to a macro in order to build the memcpy symbols in preparation for the EVA support. However, this commit also removed the NOP instruction after the 'jr ra' when returning back to the caller. This had no visible side-effects since the next instruction was a load to the t0 register which was already in the clobbered list, but it may have undesired effects in the future if some other code is introduced in between the .Ldone and the .Ll_exc_copy labels. Signed-off-by: Markos Chandras Cc: # v3.15+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8512/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index c17ef80..5d3238a 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -503,6 +503,7 @@ STOREB(t0, NBYTES-2(dst), .Ls_exc_p1\@) .Ldone\@: jr ra + nop .if __memcpy == 1 END(memcpy) .set __memcpy, 0 -- cgit v0.10.2 From 640465bda58c7078725201be7430c31a349121e9 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 18 Nov 2014 18:47:13 +0100 Subject: MIPS: Zero variable read by get_user / __get_user in case of an error. This wasn't happening in all cases. Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index b9ab717..133678a 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -301,7 +301,8 @@ do { \ __get_kernel_common((x), size, __gu_ptr); \ else \ __get_user_common((x), size, __gu_ptr); \ - } \ + } else \ + (x) = 0; \ \ __gu_err; \ }) @@ -316,6 +317,7 @@ do { \ " .insn \n" \ " .section .fixup,\"ax\" \n" \ "3: li %0, %4 \n" \ + " move %1, $0 \n" \ " j 2b \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ @@ -630,6 +632,7 @@ do { \ " .insn \n" \ " .section .fixup,\"ax\" \n" \ "3: li %0, %4 \n" \ + " move %1, $0 \n" \ " j 2b \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ -- cgit v0.10.2 From 99436f7d69045800ffd1d66912f85d37150c7e2b Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 17 Nov 2014 16:09:54 +0000 Subject: MIPS: jump_label.c: Correct the span of the J instruction Correct the check for the span of the 256MB segment addressable by the J instruction according to this instruction's semantics. The calculation of the jump target is applied to the address of the delay-slot instruction that immediately follows. Adjust the check accordingly by adding 4 to `e->code' that holds the address of the J instruction itself. Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8515/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/jump_label.c b/arch/mips/kernel/jump_label.c index 6001610..f65a843 100644 --- a/arch/mips/kernel/jump_label.c +++ b/arch/mips/kernel/jump_label.c @@ -27,8 +27,8 @@ void arch_jump_label_transform(struct jump_entry *e, union mips_instruction *insn_p = (union mips_instruction *)(unsigned long)e->code; - /* Jump only works within a 256MB aligned region. */ - BUG_ON((e->target & ~J_RANGE_MASK) != (e->code & ~J_RANGE_MASK)); + /* Jump only works within a 256MB aligned region of its delay slot. */ + BUG_ON((e->target & ~J_RANGE_MASK) != ((e->code + 4) & ~J_RANGE_MASK)); /* Target must have 4 byte alignment. */ BUG_ON((e->target & 3) != 0); -- cgit v0.10.2 From 935c2dbec4d6d3163ee8e7409996904a734ad89a Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 17 Nov 2014 16:10:32 +0000 Subject: MIPS: jump_label.c: Handle the microMIPS J instruction encoding Implement the microMIPS encoding of the J instruction for the purpose of the static keys feature, fixing a crash early on in bootstrap as the kernel is unhappy seeing the ISA bit set in jump table entries. Make sure the ISA bit correctly reflects the instruction encoding chosen for the kernel, 0 for the standard MIPS and 1 for the microMIPS encoding. Also make sure the instruction to patch is a 32-bit NOP in the microMIPS mode as by default the 16-bit short encoding is assumed Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8516/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h index e194f95..fdbff44 100644 --- a/arch/mips/include/asm/jump_label.h +++ b/arch/mips/include/asm/jump_label.h @@ -20,9 +20,15 @@ #define WORD_INSN ".word" #endif +#ifdef CONFIG_CPU_MICROMIPS +#define NOP_INSN "nop32" +#else +#define NOP_INSN "nop" +#endif + static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:\tnop\n\t" + asm_volatile_goto("1:\t" NOP_INSN "\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" WORD_INSN " 1b, %l[l_yes], %0\n\t" diff --git a/arch/mips/kernel/jump_label.c b/arch/mips/kernel/jump_label.c index f65a843..dda800e 100644 --- a/arch/mips/kernel/jump_label.c +++ b/arch/mips/kernel/jump_label.c @@ -18,31 +18,53 @@ #ifdef HAVE_JUMP_LABEL -#define J_RANGE_MASK ((1ul << 28) - 1) +/* + * Define parameters for the standard MIPS and the microMIPS jump + * instruction encoding respectively: + * + * - the ISA bit of the target, either 0 or 1 respectively, + * + * - the amount the jump target address is shifted right to fit in the + * immediate field of the machine instruction, either 2 or 1, + * + * - the mask determining the size of the jump region relative to the + * delay-slot instruction, either 256MB or 128MB, + * + * - the jump target alignment, either 4 or 2 bytes. + */ +#define J_ISA_BIT IS_ENABLED(CONFIG_CPU_MICROMIPS) +#define J_RANGE_SHIFT (2 - J_ISA_BIT) +#define J_RANGE_MASK ((1ul << (26 + J_RANGE_SHIFT)) - 1) +#define J_ALIGN_MASK ((1ul << J_RANGE_SHIFT) - 1) void arch_jump_label_transform(struct jump_entry *e, enum jump_label_type type) { + union mips_instruction *insn_p; union mips_instruction insn; - union mips_instruction *insn_p = - (union mips_instruction *)(unsigned long)e->code; - /* Jump only works within a 256MB aligned region of its delay slot. */ + insn_p = (union mips_instruction *)msk_isa16_mode(e->code); + + /* Jump only works within an aligned region its delay slot is in. */ BUG_ON((e->target & ~J_RANGE_MASK) != ((e->code + 4) & ~J_RANGE_MASK)); - /* Target must have 4 byte alignment. */ - BUG_ON((e->target & 3) != 0); + /* Target must have the right alignment and ISA must be preserved. */ + BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT); if (type == JUMP_LABEL_ENABLE) { - insn.j_format.opcode = j_op; - insn.j_format.target = (e->target & J_RANGE_MASK) >> 2; + insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op; + insn.j_format.target = e->target >> J_RANGE_SHIFT; } else { insn.word = 0; /* nop */ } get_online_cpus(); mutex_lock(&text_mutex); - *insn_p = insn; + if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) { + insn_p->halfword[0] = insn.word >> 16; + insn_p->halfword[1] = insn.word; + } else + *insn_p = insn; flush_icache_range((unsigned long)insn_p, (unsigned long)insn_p + sizeof(*insn_p)); -- cgit v0.10.2 From 49e41938f8a53d01d1c5d133a038acb9650008f9 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Tue, 18 Nov 2014 11:10:53 -0800 Subject: ARM: multi_v7_defconfig: fix failure setting CPU voltage by enabling dependent I2C controller This patch fixes a long standing issue introduced during the 3.16 merge window. Shortly after the merge, exynos5250-based arndale boards began to produce the following errors: kern.err kernel: exynos-cpufreq exynos-cpufreq: failed to set cpu voltage kern.err kernel: cpufreq: __target_index: Failed to change cpu frequency: -22 Further analysis revealed that the S5M8767 voltage regulator used on the exynos5250-based arndale board utilizes the S3C2410 I2C controller. If the S3C2410 I2C controller driver is not enabled, the S5M8767 voltage regulator fails to probe. Therefore a dependency exists between these two drivers. In the exynos_defconfig both CONFIG_REGULATOR_S5M8767 and CONFIG_I2C_S3C2410 options are enabled, and no errors are produced. However, in the multi_v7_defconfig only the CONFIG_REGULATOR_S5M8767 option is enabled and the errors are present. So let's enable the CONFIG_I2C_S3C2410 option in the multi_v7_defconfig to allow the S5M8767 voltage regulator to probe. Signed-off-by: Tyler Baker Acked-by: Kukjin Kim Signed-off-by: Kevin Hilman diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 3487046..9d7a32f 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -217,6 +217,7 @@ CONFIG_I2C_CADENCE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_EXYNOS5=y CONFIG_I2C_MV64XXX=y +CONFIG_I2C_S3C2410=y CONFIG_I2C_SIRF=y CONFIG_I2C_TEGRA=y CONFIG_I2C_ST=y -- cgit v0.10.2 From c1a2086e2d8c4eb4e8630ba752e911ec180dec67 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 19 Nov 2014 16:22:32 +0000 Subject: of/selftest: Fix off-by-one error in removal path The removal path for selftest data has an off by one error that causes the code to dereference beyond the end of the nodes[] array on the first pass through. The old code only worked by chance on a lot of platforms, but the bug was recently exposed on aarch64. The fix is simple. Decrement the node count before dereferencing, not after. Reported-by: Kevin Hilman Cc: Rob Herring Cc: Gaurav Minocha Cc: # v3.17+ diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 11b873c..e6c14dc 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -896,7 +896,7 @@ static void selftest_data_remove(void) return; } - while (last_node_index >= 0) { + while (last_node_index-- > 0) { if (nodes[last_node_index]) { np = of_find_node_by_path(nodes[last_node_index]->full_name); if (strcmp(np->full_name, "/aliases") != 0) { @@ -908,7 +908,6 @@ static void selftest_data_remove(void) } } } - last_node_index--; } } -- cgit v0.10.2 From 4a7795d35e252f38298980530e01e21867f8f856 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Wed, 19 Nov 2014 15:50:34 +0800 Subject: vfs: fix reference leak in d_prune_aliases() In "d_prune_alias(): just lock the parent and call __dentry_kill()" the old dget + d_drop + dput has been replaced with lock_parent + __dentry_kill; unfortunately, dput() does more than just killing dentry - it also drops the reference to parent. New variant leaks that reference and needs dput(parent) after killing the child off. Signed-off-by: Yan, Zheng Signed-off-by: Al Viro diff --git a/fs/dcache.c b/fs/dcache.c index 3ffef7f..5bc72b0 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -778,6 +778,7 @@ restart: struct dentry *parent = lock_parent(dentry); if (likely(!dentry->d_lockref.count)) { __dentry_kill(dentry); + dput(parent); goto restart; } if (parent) -- cgit v0.10.2 From 7ca2f234404e738cc807ed87c43f9932513bc8c6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Nov 2014 15:11:24 +0100 Subject: isofs: avoid unused function warning With the isofs_hash() function removed, isofs_hash_ms() is the only user of isofs_hash_common(), but it's defined inside of an #ifdef, which triggers this gcc warning in ARM axm55xx_defconfig starting with v3.18-rc3: fs/isofs/inode.c:177:1: warning: 'isofs_hash_common' defined but not used [-Wunused-function] This patch moves the function inside of the same #ifdef section to avoid that warning, which seems the best compromise of a relatively harmless patch for a late -rc. Signed-off-by: Arnd Bergmann Fixes: b0afd8e5db7b ("isofs: don't bother with ->d_op for normal case") Signed-off-by: Al Viro diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index fe839b9..d67a16f 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -174,27 +174,6 @@ struct iso9660_options{ * Compute the hash for the isofs name corresponding to the dentry. */ static int -isofs_hash_common(struct qstr *qstr, int ms) -{ - const char *name; - int len; - - len = qstr->len; - name = qstr->name; - if (ms) { - while (len && name[len-1] == '.') - len--; - } - - qstr->hash = full_name_hash(name, len); - - return 0; -} - -/* - * Compute the hash for the isofs name corresponding to the dentry. - */ -static int isofs_hashi_common(struct qstr *qstr, int ms) { const char *name; @@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, } #ifdef CONFIG_JOLIET +/* + * Compute the hash for the isofs name corresponding to the dentry. + */ +static int +isofs_hash_common(struct qstr *qstr, int ms) +{ + const char *name; + int len; + + len = qstr->len; + name = qstr->name; + if (ms) { + while (len && name[len-1] == '.') + len--; + } + + qstr->hash = full_name_hash(name, len); + + return 0; +} + static int isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) { -- cgit v0.10.2 From f82c458a2c3ffb94b431fc6ad791a79df1b3713e Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 19 Nov 2014 10:25:09 -0800 Subject: btrfs: fix lockups from btrfs_clear_path_blocking The fair reader/writer locks mean that btrfs_clear_path_blocking needs to strictly follow lock ordering rules even when we already have blocking locks on a given path. Before we can clear a blocking lock on the path, we need to make sure all of the locks have been converted to blocking. This will remove lock inversions against anyone spinning in write_lock() against the buffers we're trying to get read locks on. These inversions didn't exist before the fair read/writer locks, but now we need to be more careful. We papered over this deadlock in the past by changing btrfs_try_read_lock() to be a true trylock against both the spinlock and the blocking lock. This was slower, and not sufficient to fix all the deadlocks. This patch adds a btrfs_tree_read_lock_atomic(), which basically means get the spinlock but trylock on the blocking lock. Signed-off-by: Chris Mason Signed-off-by: Josef Bacik Reported-by: Patrick Schmid cc: stable@vger.kernel.org #v3.15+ diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 19bc616..150822e 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -80,13 +80,6 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p, { int i; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - /* lockdep really cares that we take all of these spinlocks - * in the right order. If any of the locks in the path are not - * currently blocking, it is going to complain. So, make really - * really sure by forcing the path to blocking before we clear - * the path blocking. - */ if (held) { btrfs_set_lock_blocking_rw(held, held_rw); if (held_rw == BTRFS_WRITE_LOCK) @@ -95,7 +88,6 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p, held_rw = BTRFS_READ_LOCK_BLOCKING; } btrfs_set_path_blocking(p); -#endif for (i = BTRFS_MAX_LEVEL - 1; i >= 0; i--) { if (p->nodes[i] && p->locks[i]) { @@ -107,10 +99,8 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p, } } -#ifdef CONFIG_DEBUG_LOCK_ALLOC if (held) btrfs_clear_lock_blocking_rw(held, held_rw); -#endif } /* this also releases the path */ @@ -2893,7 +2883,7 @@ cow_done: } p->locks[level] = BTRFS_WRITE_LOCK; } else { - err = btrfs_try_tree_read_lock(b); + err = btrfs_tree_read_lock_atomic(b); if (!err) { btrfs_set_path_blocking(p); btrfs_tree_read_lock(b); @@ -3025,7 +3015,7 @@ again: } level = btrfs_header_level(b); - err = btrfs_try_tree_read_lock(b); + err = btrfs_tree_read_lock_atomic(b); if (!err) { btrfs_set_path_blocking(p); btrfs_tree_read_lock(b); diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index 5665d21..f8229ef 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -128,6 +128,26 @@ again: } /* + * take a spinning read lock. + * returns 1 if we get the read lock and 0 if we don't + * this won't wait for blocking writers + */ +int btrfs_tree_read_lock_atomic(struct extent_buffer *eb) +{ + if (atomic_read(&eb->blocking_writers)) + return 0; + + read_lock(&eb->lock); + if (atomic_read(&eb->blocking_writers)) { + read_unlock(&eb->lock); + return 0; + } + atomic_inc(&eb->read_locks); + atomic_inc(&eb->spinning_readers); + return 1; +} + +/* * returns 1 if we get the read lock and 0 if we don't * this won't wait for blocking writers */ @@ -158,9 +178,7 @@ int btrfs_try_tree_write_lock(struct extent_buffer *eb) atomic_read(&eb->blocking_readers)) return 0; - if (!write_trylock(&eb->lock)) - return 0; - + write_lock(&eb->lock); if (atomic_read(&eb->blocking_writers) || atomic_read(&eb->blocking_readers)) { write_unlock(&eb->lock); diff --git a/fs/btrfs/locking.h b/fs/btrfs/locking.h index b81e0e9..c44a9d5 100644 --- a/fs/btrfs/locking.h +++ b/fs/btrfs/locking.h @@ -35,6 +35,8 @@ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw); void btrfs_assert_tree_locked(struct extent_buffer *eb); int btrfs_try_tree_read_lock(struct extent_buffer *eb); int btrfs_try_tree_write_lock(struct extent_buffer *eb); +int btrfs_tree_read_lock_atomic(struct extent_buffer *eb); + static inline void btrfs_tree_unlock_rw(struct extent_buffer *eb, int rw) { -- cgit v0.10.2 From b8e4500f42fe4464a33a887579147050bed8fcef Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 18 Nov 2014 15:14:44 +0100 Subject: bonding: fix curr_active_slave/carrier with loadbalance arp monitoring Since commit 6fde8f037e60 ("bonding: fix locking in bond_loadbalance_arp_mon()") we can have a stale bond carrier state and stale curr_active_slave when using arp monitoring in loadbalance modes. The reason is that in bond_loadbalance_arp_mon() we can't have do_failover == true but slave_state_changed == false, whenever do_failover is true then slave_state_changed is also true. Then the following piece from bond_loadbalance_arp_mon(): if (slave_state_changed) { bond_slave_state_change(bond); if (BOND_MODE(bond) == BOND_MODE_XOR) bond_update_slave_arr(bond, NULL); } else if (do_failover) { block_netpoll_tx(); bond_select_active_slave(bond); unblock_netpoll_tx(); } will execute only the first branch, always and regardless of do_failover. Since these two events aren't related in such way, we need to decouple and consider them separately. For example this issue could lead to the following result: Bonding Mode: load balancing (round-robin) *MII Status: down* MII Polling Interval (ms): 0 Up Delay (ms): 0 Down Delay (ms): 0 ARP Polling Interval (ms): 100 ARP IP target/s (n.n.n.n form): 192.168.9.2 Slave Interface: ens12 *MII Status: up* Speed: 10000 Mbps Duplex: full Link Failure Count: 2 Permanent HW addr: 00:0f:53:01:42:2c Slave queue ID: 0 Slave Interface: eth1 *MII Status: up* Speed: Unknown Duplex: Unknown Link Failure Count: 70 Permanent HW addr: 52:54:00:2f:0f:8e Slave queue ID: 0 Since some interfaces are up, then the status of the bond should also be up, but it will never change unless something invokes bond_set_carrier() (i.e. enslave, bond_select_active_slave etc). Now, if I force the calling of bond_select_active_slave via for example changing primary_reselect (it can change in any mode), then the MII status goes to "up" because it calls bond_select_active_slave() which should've been done from bond_loadbalance_arp_mon() itself. CC: Veaceslav Falico CC: Jay Vosburgh CC: Andy Gospodarek CC: Ding Tianhong Fixes: 6fde8f037e60 ("bonding: fix locking in bond_loadbalance_arp_mon()") Signed-off-by: Nikolay Aleksandrov Acked-by: Veaceslav Falico Acked-by: Andy Gospodarek Acked-by: Ding Tianhong Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c9ac06c..a5115fb 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2471,7 +2471,8 @@ static void bond_loadbalance_arp_mon(struct work_struct *work) bond_slave_state_change(bond); if (BOND_MODE(bond) == BOND_MODE_XOR) bond_update_slave_arr(bond, NULL); - } else if (do_failover) { + } + if (do_failover) { block_netpoll_tx(); bond_select_active_slave(bond); unblock_netpoll_tx(); -- cgit v0.10.2 From 9737c6ab7afbc950e997ef80cba2c40dbbd16ea4 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 18 Nov 2014 17:51:27 +0200 Subject: net/mlx4_en: Add VXLAN ndo calls to the PF net device ops too This is currently missing, which results in a crash when one attempts to set VXLAN tunnel over the mlx4_en when acting as PF. [ 2408.785472] BUG: unable to handle kernel NULL pointer dereference at (null) [...] [ 2408.994104] Call Trace: [ 2408.996584] [] ? vxlan_get_rx_port+0xd6/0x103 [vxlan] [ 2409.003316] [] ? vxlan_lowerdev_event+0xf2/0xf2 [vxlan] [ 2409.010225] [] mlx4_en_start_port+0x862/0x96a [mlx4_en] [ 2409.017132] [] mlx4_en_open+0x17f/0x1b8 [mlx4_en] While here, make sure to invoke vxlan_get_rx_port() only when VXLAN offloads are actually enabled and not when they are only supported. Reported-by: Ido Shamay Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index c5fcc56..4d69e38 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1693,7 +1693,7 @@ int mlx4_en_start_port(struct net_device *dev) mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap); #ifdef CONFIG_MLX4_EN_VXLAN - if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS) + if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) vxlan_get_rx_port(dev); #endif priv->port_up = true; @@ -2422,6 +2422,11 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_rx_flow_steer = mlx4_en_filter_rfs, #endif .ndo_get_phys_port_id = mlx4_en_get_phys_port_id, +#ifdef CONFIG_MLX4_EN_VXLAN + .ndo_add_vxlan_port = mlx4_en_add_vxlan_port, + .ndo_del_vxlan_port = mlx4_en_del_vxlan_port, + .ndo_gso_check = mlx4_en_gso_check, +#endif }; int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, -- cgit v0.10.2 From c6c15e1ed303ffc47e696ea1c9a9df1761c1f603 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 19 Nov 2014 12:47:50 -0500 Subject: nfsd: Fix slot wake up race in the nfsv4.1 callback code The currect code for nfsd41_cb_get_slot() and nfsd4_cb_done() has no locking in order to guarantee atomicity, and so allows for races of the form. Task 1 Task 2 ====== ====== if (test_and_set_bit(0) != 0) { clear_bit(0) rpc_wake_up_next(queue) rpc_sleep_on(queue) return false; } This patch breaks the race condition by adding a retest of the bit after the call to rpc_sleep_on(). Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index ed2b115..7cbdf1b 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -774,8 +774,12 @@ static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task) { if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); - dprintk("%s slot is busy\n", __func__); - return false; + /* Race breaker */ + if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { + dprintk("%s slot is busy\n", __func__); + return false; + } + rpc_wake_up_queued_task(&clp->cl_cb_waitq, task); } return true; } -- cgit v0.10.2 From 7fc986d8a9727e5d40da3c2c1c343da6142e82a9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 19 Nov 2014 14:30:32 -0700 Subject: PCI: Support 64-bit bridge windows if we have 64-bit dma_addr_t Aaron reported that a 32-bit x86 kernel with Physical Address Extension (PAE) support complains about bridge prefetchable memory windows above 4GB: pci_bus 0000:00: root bus resource [mem 0x380000000000-0x383fffffffff] ... pci 0000:03:00.0: reg 0x10: [mem 0x383fffc00000-0x383fffdfffff 64bit pref] pci 0000:03:00.0: reg 0x20: [mem 0x383fffe04000-0x383fffe07fff 64bit pref] pci 0000:03:00.1: reg 0x10: [mem 0x383fffa00000-0x383fffbfffff 64bit pref] pci 0000:03:00.1: reg 0x20: [mem 0x383fffe00000-0x383fffe03fff 64bit pref] pci 0000:00:02.2: PCI bridge to [bus 03-04] pci 0000:00:02.2: bridge window [io 0x1000-0x1fff] pci 0000:00:02.2: bridge window [mem 0x91900000-0x91cfffff] pci 0000:00:02.2: can't handle 64-bit address space for bridge In this kernel, unsigned long is 32 bits and dma_addr_t is 64 bits. Previously we used "unsigned long" to hold the bridge window address. But this is a bus address, so we should use dma_addr_t instead. Use dma_addr_t to hold the bridge window base and limit. The question of whether the CPU can actually *address* the window is separate and depends on what the physical address space of the CPU is and whether the host bridge does any address translation. [bhelgaas: fix "shift count > width of type", changelog, stable tag] Fixes: d56dbf5bab8c ("PCI: Allocate 64-bit BARs above 4G when possible") Link: https://bugzilla.kernel.org/show_bug.cgi?id=88131 Reported-by: Aaron Ma Tested-by: Aaron Ma Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.14+ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6244b18..c8ca98c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -407,15 +407,16 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child) { struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; - unsigned long base, limit; + u64 base64, limit64; + dma_addr_t base, limit; struct pci_bus_region region; struct resource *res; res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); - base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16; - limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; + base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; + limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { u32 mem_base_hi, mem_limit_hi; @@ -429,17 +430,20 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child) * this, just assume they are not being used. */ if (mem_base_hi <= mem_limit_hi) { -#if BITS_PER_LONG == 64 - base |= ((unsigned long) mem_base_hi) << 32; - limit |= ((unsigned long) mem_limit_hi) << 32; -#else - if (mem_base_hi || mem_limit_hi) { - dev_err(&dev->dev, "can't handle 64-bit address space for bridge\n"); - return; - } -#endif + base64 |= (u64) mem_base_hi << 32; + limit64 |= (u64) mem_limit_hi << 32; } } + + base = (dma_addr_t) base64; + limit = (dma_addr_t) limit64; + + if (base != base64) { + dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n", + (unsigned long long) base64); + return; + } + if (base <= limit) { res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; -- cgit v0.10.2 From ffb1388a364d135810337182d6800a0c7ee44f48 Mon Sep 17 00:00:00 2001 From: Duan Jiong Date: Wed, 19 Nov 2014 09:35:39 +0800 Subject: ipv6: delete protocol and unregister rtnetlink when cleanup pim6_protocol was added when initiation, but it not deleted. Similarly, unregister RTNL_FAMILY_IP6MR rtnetlink. Signed-off-by: Duan Jiong Reviewed-by: Cong Wang Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 0171f08..1a01d79 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1439,6 +1439,10 @@ reg_pernet_fail: void ip6_mr_cleanup(void) { + rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE); +#ifdef CONFIG_IPV6_PIMSM_V2 + inet6_del_protocol(&pim6_protocol, IPPROTO_PIM); +#endif unregister_netdevice_notifier(&ip6_mr_notifier); unregister_pernet_subsys(&ip6mr_net_ops); kmem_cache_destroy(mrt_cachep); -- cgit v0.10.2 From ee7255ada313a6db99be47ce174b0bfb8295a041 Mon Sep 17 00:00:00 2001 From: Anish Bhatt Date: Tue, 18 Nov 2014 19:09:51 -0800 Subject: cxgb4i : Don't block unload/cxgb4 unload when remote closes TCP connection cxgb4i was returning wrong error and not releasing module reference if remote end abruptly closed TCP connection. This prevents the cxgb4 network module from being unloaded, further affecting other network drivers dependent on cxgb4 Sending to net as this affects all cxgb4 based network drivers. Signed-off-by: Anish Bhatt Signed-off-by: David S. Miller diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 3e0a0d3..f48f40ce 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -828,6 +828,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb) if (status == CPL_ERR_RTX_NEG_ADVICE) goto rel_skb; + module_put(THIS_MODULE); + if (status && status != CPL_ERR_TCAM_FULL && status != CPL_ERR_CONN_EXIST && status != CPL_ERR_ARP_MISS) diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 674d498..9d63853 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -816,7 +816,7 @@ static void cxgbi_inform_iscsi_conn_closing(struct cxgbi_sock *csk) read_lock_bh(&csk->callback_lock); if (csk->user_data) iscsi_conn_failure(csk->user_data, - ISCSI_ERR_CONN_FAILED); + ISCSI_ERR_TCP_CONN_CLOSE); read_unlock_bh(&csk->callback_lock); } } -- cgit v0.10.2 From 6b19b66013cfe608d63f0dab38834bbaceb0217a Mon Sep 17 00:00:00 2001 From: Arnaud Ebalard Date: Wed, 19 Nov 2014 22:52:36 +0100 Subject: hwmon: (g762) fix call to devm_hwmon_device_register_with_groups() g762_remove() needs to first call hwmon_device_unregister() and then g762_of_clock_disable(). For that reason, it is not possible to convert it to devm_hwmon_device_register_with_groups() and the the non device managed version must be used. This is correctly stated in commit message for 398e16db6262 ("hwmon: (g762) Convert to hwmon_device_register_with_groups") but the associated changes do in fact introduce a call to the device managed version of the function. This patch fixes that typo by switching to the non devm_ version. Fixes: 398e16db6262 ("hwmon: (g762) Convert to hwmon_device_register_with_groups") Cc: stable@vger.kernel.org (3.17+) Signed-off-by: Arnaud Ebalard Signed-off-by: Guenter Roeck diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 6aac695..9b55e67 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -1084,10 +1084,8 @@ static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) goto clock_dis; - data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, - data, - g762_groups); + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, g762_groups); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); goto clock_dis; -- cgit v0.10.2 From 78579b7c7eb45f0e7ec5e9437087ed21749f9a9c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 19 Nov 2014 01:44:11 +0100 Subject: ACPI / PM: Ignore wakeup setting if the ACPI companion can't wake up As reported by Dmitry, on some Chromebooks there are devices with corresponding ACPI objects and with unusual system wakeup configuration. Namely, they technically are wakeup-capable, but the wakeup is handled via a platform-specific out-of-band mechanism and the ACPI PM layer has no information on the wakeup capability. As a result, device_may_wakeup(dev) called from acpi_dev_suspend_late() returns 'true' for those devices, but the wakeup.flags.valid flag is unset for the corresponding ACPI device objects, so acpi_device_wakeup() reproducibly fails for them causing acpi_dev_suspend_late() to return an error code. The entire system suspend is then aborted and the machines in question cannot suspend at all. Address the problem by ignoring the device_may_wakeup(dev) return value in acpi_dev_suspend_late() if the ACPI companion of the device being handled has wakeup.flags.valid unset (in which case it is clear that the wakeup is supposed to be handled by other means). This fixes a regression introduced by commit a76e9bd89ae7 (i2c: attach/detach I2C client device to the ACPI power domain) as the affected systems could suspend and resume successfully before that commit. Fixes: a76e9bd89ae7 (i2c: attach/detach I2C client device to the ACPI power domain) Reported-by: Dmitry Torokhov Reviewed-by: Dmitry Torokhov Cc: 3.13+ # 3.13+ Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 143ec6e..7db1931 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -878,7 +878,7 @@ int acpi_dev_suspend_late(struct device *dev) return 0; target_state = acpi_target_system_state(); - wakeup = device_may_wakeup(dev); + wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); error = acpi_device_wakeup(adev, target_state, wakeup); if (wakeup && error) return error; -- cgit v0.10.2 From b1a5ad006b34ded9dc7ec64988deba1b3ecad367 Mon Sep 17 00:00:00 2001 From: Chris Moore Date: Tue, 4 Nov 2014 16:28:29 +0000 Subject: IB/isert: Adjust CQ size to HW limits isert has an issue of trying to create a CQ with more CQEs than are supported by the hardware, that currently results in failures during isert_device creation during first session login. This is the isert version of the patch that Minh Tran submitted for iser, and is simple a workaround required to function with existing ocrdma hardware. Signed-off-by: Chris Moore Reviewied-by: Sagi Grimberg Cc: # 3.10+ Signed-off-by: Nicholas Bellinger diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index a6c7b39..10641b7 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -228,12 +228,16 @@ isert_create_device_ib_res(struct isert_device *device) struct isert_cq_desc *cq_desc; struct ib_device_attr *dev_attr; int ret = 0, i, j; + int max_rx_cqe, max_tx_cqe; dev_attr = &device->dev_attr; ret = isert_query_device(ib_dev, dev_attr); if (ret) return ret; + max_rx_cqe = min(ISER_MAX_RX_CQ_LEN, dev_attr->max_cqe); + max_tx_cqe = min(ISER_MAX_TX_CQ_LEN, dev_attr->max_cqe); + /* asign function handlers */ if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS && dev_attr->device_cap_flags & IB_DEVICE_SIGNATURE_HANDOVER) { @@ -275,7 +279,7 @@ isert_create_device_ib_res(struct isert_device *device) isert_cq_rx_callback, isert_cq_event_callback, (void *)&cq_desc[i], - ISER_MAX_RX_CQ_LEN, i); + max_rx_cqe, i); if (IS_ERR(device->dev_rx_cq[i])) { ret = PTR_ERR(device->dev_rx_cq[i]); device->dev_rx_cq[i] = NULL; @@ -287,7 +291,7 @@ isert_create_device_ib_res(struct isert_device *device) isert_cq_tx_callback, isert_cq_event_callback, (void *)&cq_desc[i], - ISER_MAX_TX_CQ_LEN, i); + max_tx_cqe, i); if (IS_ERR(device->dev_tx_cq[i])) { ret = PTR_ERR(device->dev_tx_cq[i]); device->dev_tx_cq[i] = NULL; -- cgit v0.10.2 From 0f32fd1900e6b972f289416dbd75e92772b630cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Nov 2014 12:16:14 +0100 Subject: ALSA: hda/realtek - Clean up mute/mic GPIO LED handling There are a few duplicated codes handling the mute and mic-mute LEDs via GPIO pins. Let's consolidate to single helpers. Here we introduced two new fields to alc_spec, gpio_mute_led_mask and gpio_mic_led_mask, to contain the bit mask to set/clear. Also, mute_led_polarity is evaluated as well. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1af917f..3c29a55 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -96,6 +96,8 @@ struct alc_spec { hda_nid_t cap_mute_led_nid; unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */ + unsigned int gpio_mute_led_mask; + unsigned int gpio_mic_led_mask; hda_nid_t headset_mic_pin; hda_nid_t headphone_mic_pin; @@ -3235,41 +3237,45 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, } } -/* turn on/off mute LED per vmaster hook */ -static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled) +/* update LED status via GPIO */ +static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + bool enabled) { - struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; unsigned int oldval = spec->gpio_led; + if (spec->mute_led_polarity) + enabled = !enabled; + if (enabled) - spec->gpio_led &= ~0x08; + spec->gpio_led &= ~mask; else - spec->gpio_led |= 0x08; + spec->gpio_led |= mask; if (spec->gpio_led != oldval) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); } -/* turn on/off mic-mute LED per capture hook */ -static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* turn on/off mute LED via GPIO per vmaster hook */ +static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) { + struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; - if (!ucontrol) - return; + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); +} - if (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]) - spec->gpio_led &= ~0x10; - else - spec->gpio_led |= 0x10; - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); +/* turn on/off mic-mute LED via GPIO per capture hook */ +static void alc_fixup_gpio_mic_mute_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct alc_spec *spec = codec->spec; + + if (ucontrol) + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]); } static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, @@ -3283,9 +3289,12 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; - spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; + spec->gpio_mic_led_mask = 0x10; snd_hda_add_verbs(codec, gpio_init); } } @@ -3327,9 +3336,11 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; spec->cap_mute_led_nid = 0x18; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = led_power_filter; @@ -3348,9 +3359,11 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; spec->cap_mute_led_nid = 0x18; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = led_power_filter; @@ -5624,22 +5637,6 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec, } } -/* turn on/off mute LED per vmaster hook */ -static void alc662_led_gpio1_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; - - if (enabled) - spec->gpio_led |= 0x01; - else - spec->gpio_led &= ~0x01; - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); -} - /* avoid D3 for keeping GPIO up */ static unsigned int gpio_led_power_filter(struct hda_codec *codec, hda_nid_t nid, @@ -5662,8 +5659,10 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc662_led_gpio1_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 1; + spec->gpio_mute_led_mask = 0x01; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = gpio_led_power_filter; } -- cgit v0.10.2 From 788ec2fc2ca295a2d929986e95231214ecd8d142 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 19 Nov 2014 17:13:44 +0000 Subject: of/selftest: Fix testing when /aliases is missing The /aliases node isn't always present in the device tree, but the unittest code assumes that /aliases is there. Add a check when inserting the testcase data to see if of_aliases needs to be updated, and undo the settings when the nodes are removed. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Gaurav Minocha Cc: diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index e6c14dc..e2d79af 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -899,7 +899,11 @@ static void selftest_data_remove(void) while (last_node_index-- > 0) { if (nodes[last_node_index]) { np = of_find_node_by_path(nodes[last_node_index]->full_name); - if (strcmp(np->full_name, "/aliases") != 0) { + if (np == nodes[last_node_index]) { + if (of_aliases == np) { + of_node_put(of_aliases); + of_aliases = NULL; + } detach_node_and_children(np); } else { for_each_property_of_node(np, prop) { @@ -920,6 +924,8 @@ static int __init of_selftest(void) res = selftest_data_add(); if (res) return res; + if (!of_aliases) + of_aliases = of_find_node_by_path("/aliases"); np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); if (!np) { -- cgit v0.10.2 From ef94b1864d1ed5be54376404bb23d22ed0481feb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:39:59 +0100 Subject: ovl: rename filesystem type to "overlay" Some distributions carry an "old" format of overlayfs while mainline has a "new" format. The distros will possibly want to keep the old overlayfs alongside the new for compatibility reasons. To make it possible to differentiate the two versions change the name of the new one from "overlayfs" to "overlay". Signed-off-by: Miklos Szeredi Reported-by: Serge Hallyn Cc: Andy Whitcroft diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt index 530850a..a27c950 100644 --- a/Documentation/filesystems/overlayfs.txt +++ b/Documentation/filesystems/overlayfs.txt @@ -64,7 +64,7 @@ is formed. At mount time, the two directories given as mount options "lowerdir" and "upperdir" are combined into a merged directory: - mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\ + mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\ workdir=/work /merged The "workdir" needs to be an empty directory on the same filesystem diff --git a/MAINTAINERS b/MAINTAINERS index c444907..af1e738 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6888,7 +6888,7 @@ F: drivers/scsi/osd/ F: include/scsi/osd_* F: fs/exofs/ -OVERLAYFS FILESYSTEM +OVERLAY FILESYSTEM M: Miklos Szeredi L: linux-fsdevel@vger.kernel.org S: Supported diff --git a/fs/Makefile b/fs/Makefile index 34a1b9de..da0bbb4 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/ obj-$(CONFIG_AUTOFS4_FS) += autofs4/ obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ -obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/ +obj-$(CONFIG_OVERLAY_FS) += overlayfs/ obj-$(CONFIG_UDF_FS) += udf/ obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ obj-$(CONFIG_OMFS_FS) += omfs/ diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig index e601259..3435581 100644 --- a/fs/overlayfs/Kconfig +++ b/fs/overlayfs/Kconfig @@ -1,4 +1,4 @@ -config OVERLAYFS_FS +config OVERLAY_FS tristate "Overlay filesystem support" help An overlay filesystem combines two filesystems - an 'upper' filesystem diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile index 8f91889..900daed 100644 --- a/fs/overlayfs/Makefile +++ b/fs/overlayfs/Makefile @@ -2,6 +2,6 @@ # Makefile for the overlay filesystem. # -obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o +obj-$(CONFIG_OVERLAY_FS) += overlay.o -overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o +overlay-objs := super.o inode.o dir.o readdir.o copy_up.o diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 08b704c..b92bd18 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -24,7 +24,7 @@ MODULE_AUTHOR("Miklos Szeredi "); MODULE_DESCRIPTION("Overlay filesystem"); MODULE_LICENSE("GPL"); -#define OVERLAYFS_SUPER_MAGIC 0x794c764f +#define OVERLAYFS_SUPER_MAGIC 0x794c7630 struct ovl_config { char *lowerdir; @@ -776,11 +776,11 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, static struct file_system_type ovl_fs_type = { .owner = THIS_MODULE, - .name = "overlayfs", + .name = "overlay", .mount = ovl_mount, .kill_sb = kill_anon_super, }; -MODULE_ALIAS_FS("overlayfs"); +MODULE_ALIAS_FS("overlay"); static int __init ovl_init(void) { -- cgit v0.10.2 From a105d685a8483985a01776411de191a726b48132 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:39:59 +0100 Subject: ovl: fix remove/copy-up race ovl_remove_and_whiteout() needs to check if upper dentry exists or not after having locked upper parent directory. Previously we used a "type" value computed before locking the upper parent directory, which is susceptible to racing with copy-up. There's a similar check in ovl_check_empty_and_clear(). This one is not actually racy, since copy-up doesn't change the "emptyness" property of a directory. Add a comment to this effect, and check the existence of upper dentry locally to make the code cleaner. Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 15cd91a..8ffc4b9 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -284,8 +284,7 @@ out: return ERR_PTR(err); } -static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, - enum ovl_path_type type) +static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) { int err; struct dentry *ret = NULL; @@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry, err = ovl_check_empty_dir(dentry, &list); if (err) ret = ERR_PTR(err); - else if (type == OVL_PATH_MERGE) - ret = ovl_clear_empty(dentry, &list); + else { + /* + * If no upperdentry then skip clearing whiteouts. + * + * Can race with copy-up, since we don't hold the upperdir + * mutex. Doesn't matter, since copy-up can't create a + * non-empty directory from an empty one. + */ + if (ovl_dentry_upper(dentry)) + ret = ovl_clear_empty(dentry, &list); + } ovl_cache_free(&list); @@ -487,8 +495,7 @@ out: return err; } -static int ovl_remove_and_whiteout(struct dentry *dentry, - enum ovl_path_type type, bool is_dir) +static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; @@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, int err; if (is_dir) { - opaquedir = ovl_check_empty_and_clear(dentry, type); + opaquedir = ovl_check_empty_and_clear(dentry); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) goto out; @@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, if (IS_ERR(whiteout)) goto out_unlock; - if (type == OVL_PATH_LOWER) { + upper = ovl_dentry_upper(dentry); + if (!upper) { upper = lookup_one_len(dentry->d_name.name, upperdir, - dentry->d_name.len); + dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) goto kill_whiteout; @@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, } else { int flags = 0; - upper = ovl_dentry_upper(dentry); if (opaquedir) upper = opaquedir; err = -ESTALE; @@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) cap_raise(override_cred->cap_effective, CAP_CHOWN); old_cred = override_creds(override_cred); - err = ovl_remove_and_whiteout(dentry, type, is_dir); + err = ovl_remove_and_whiteout(dentry, is_dir); revert_creds(old_cred); put_cred(override_cred); @@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, } if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { - opaquedir = ovl_check_empty_and_clear(new, new_type); + opaquedir = ovl_check_empty_and_clear(new); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) { opaquedir = NULL; -- cgit v0.10.2 From 521484639ec19a6f1ed56de6993feb255f5f676c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:00 +0100 Subject: ovl: fix race in private xattr checks Xattr operations can race with copy up. This does not matter as long as we consistently fiter out "trunsted.overlay.opaque" attribute on upper directories. Previously we checked parent against OVL_PATH_MERGE. This is too general, and prone to race with copy-up. I.e. we found the parent to be on the lower layer but ovl_dentry_real() would return the copied-up dentry, possibly with the "opaque" attribute. So instead use ovl_path_real() and decide to filter the attributes based on the actual type of the dentry we'll use. Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index af2d18c..07d74b2 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -235,26 +235,36 @@ out: return err; } +static bool ovl_need_xattr_filter(struct dentry *dentry, + enum ovl_path_type type) +{ + return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode); +} + ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { - if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && - ovl_is_private_xattr(name)) + struct path realpath; + enum ovl_path_type type = ovl_path_real(dentry, &realpath); + + if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) return -ENODATA; - return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); + return vfs_getxattr(realpath.dentry, name, value, size); } ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) { + struct path realpath; + enum ovl_path_type type = ovl_path_real(dentry, &realpath); ssize_t res; int off; - res = vfs_listxattr(ovl_dentry_real(dentry), list, size); + res = vfs_listxattr(realpath.dentry, list, size); if (res <= 0 || size == 0) return res; - if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) + if (!ovl_need_xattr_filter(dentry, type)) return res; /* filter out private xattrs */ @@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; - enum ovl_path_type type; + enum ovl_path_type type = ovl_path_real(dentry, &realpath); err = ovl_want_write(dentry); if (err) goto out; - if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && - ovl_is_private_xattr(name)) + err = -ENODATA; + if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) goto out_drop_write; - type = ovl_path_real(dentry, &realpath); if (type == OVL_PATH_LOWER) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) -- cgit v0.10.2 From 91c77947133f7aef851b625701e182d3f99d14a9 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:00 +0100 Subject: ovl: allow filenames with comma Allow option separator (comma) to be escaped with backslash. Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index b92bd18..eee7a62 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -462,11 +462,34 @@ static const match_table_t ovl_tokens = { {OPT_ERR, NULL} }; +static char *ovl_next_opt(char **s) +{ + char *sbegin = *s; + char *p; + + if (sbegin == NULL) + return NULL; + + for (p = sbegin; *p; p++) { + if (*p == '\\') { + p++; + if (!*p) + break; + } else if (*p == ',') { + *p = '\0'; + *s = p + 1; + return sbegin; + } + } + *s = NULL; + return sbegin; +} + static int ovl_parse_opt(char *opt, struct ovl_config *config) { char *p; - while ((p = strsep(&opt, ",")) != NULL) { + while ((p = ovl_next_opt(&opt)) != NULL) { int token; substring_t args[MAX_OPT_ARGS]; @@ -554,15 +577,34 @@ out_dput: goto out_unlock; } +static void ovl_unescape(char *s) +{ + char *d = s; + + for (;; s++, d++) { + if (*s == '\\') + s++; + *d = *s; + if (!*s) + break; + } +} + static int ovl_mount_dir(const char *name, struct path *path) { int err; + char *tmp = kstrdup(name, GFP_KERNEL); + + if (!tmp) + return -ENOMEM; - err = kern_path(name, LOOKUP_FOLLOW, path); + ovl_unescape(tmp); + err = kern_path(tmp, LOOKUP_FOLLOW, path); if (err) { - pr_err("overlayfs: failed to resolve '%s': %i\n", name, err); + pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err); err = -EINVAL; } + kfree(tmp); return err; } -- cgit v0.10.2 From 71d509280f7e92eb60ae6b7c78c20afafff060c7 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:01 +0100 Subject: ovl: use lockless_dereference() for upperdentry Don't open code lockless_dereference() in ovl_upperdentry_dereference(). Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index eee7a62..f16d318 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe) { - struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry); - /* - * Make sure to order reads to upperdentry wrt ovl_dentry_update() - */ - smp_read_barrier_depends(); - return upperdentry; + return lockless_dereference(oe->__upperdentry); } void ovl_path_upper(struct dentry *dentry, struct path *path) -- cgit v0.10.2 From c9f00fdb9ab3999cb2fb582ad82a5db9e70c82f5 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:01 +0100 Subject: ovl: pass dentry into ovl_dir_read_merged() Pass dentry into ovl_dir_read_merged() insted of upperpath and lowerpath. This cleans up callers and paves the way for multi-layer directory reads. Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 2a7ef4f..7299e96 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir, return 0; } -static inline int ovl_dir_read_merged(struct path *upperpath, - struct path *lowerpath, - struct list_head *list) +static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) { int err; + struct path lowerpath; + struct path upperpath; struct ovl_readdir_data rdd = { .ctx.actor = ovl_fill_merge, .list = list, @@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath, .is_merge = false, }; - if (upperpath->dentry) { - err = ovl_dir_read(upperpath, &rdd); + ovl_path_lower(dentry, &lowerpath); + ovl_path_upper(dentry, &upperpath); + + if (upperpath.dentry) { + err = ovl_dir_read(&upperpath, &rdd); if (err) goto out; - if (lowerpath->dentry) { - err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd); + if (lowerpath.dentry) { + err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd); if (err) goto out; } } - if (lowerpath->dentry) { + if (lowerpath.dentry) { /* * Insert lowerpath entries before upperpath ones, this allows * offsets to be reasonably constant */ list_add(&rdd.middle, rdd.list); rdd.is_merge = true; - err = ovl_dir_read(lowerpath, &rdd); + err = ovl_dir_read(&lowerpath, &rdd); list_del(&rdd.middle); } out: @@ -329,8 +332,6 @@ static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) { int res; - struct path lowerpath; - struct path upperpath; struct ovl_dir_cache *cache; cache = ovl_dir_cache(dentry); @@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) cache->refcount = 1; INIT_LIST_HEAD(&cache->entries); - ovl_path_lower(dentry, &lowerpath); - ovl_path_upper(dentry, &upperpath); - - res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries); + res = ovl_dir_read_merged(dentry, &cache->entries); if (res) { ovl_cache_free(&cache->entries); kfree(cache); @@ -538,14 +536,9 @@ const struct file_operations ovl_dir_operations = { int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) { int err; - struct path lowerpath; - struct path upperpath; struct ovl_cache_entry *p; - ovl_path_upper(dentry, &upperpath); - ovl_path_lower(dentry, &lowerpath); - - err = ovl_dir_read_merged(&upperpath, &lowerpath, list); + err = ovl_dir_read_merged(dentry, list); if (err) return err; -- cgit v0.10.2 From 1d113735ecf21de74a04c3b58fa106ac2e64ca0d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:01 +0100 Subject: ovl: update MAINTAINERS There's a union/overlay specific mailing list now. Also add a git tree. Signed-off-by: Miklos Szeredi diff --git a/MAINTAINERS b/MAINTAINERS index af1e738..0ff630d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6890,9 +6890,10 @@ F: fs/exofs/ OVERLAY FILESYSTEM M: Miklos Szeredi -L: linux-fsdevel@vger.kernel.org +L: linux-unionfs@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git S: Supported -F: fs/overlayfs/* +F: fs/overlayfs/ F: Documentation/filesystems/overlayfs.txt P54 WIRELESS DRIVER -- cgit v0.10.2 From 7676895f4736421ebafc48de5078e25ea69e88ee Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Nov 2014 16:40:02 +0100 Subject: ovl: ovl_dir_fsync() cleanup Check against !OVL_PATH_LOWER instead of OVL_PATH_MERGE. For a copied up directory the two are currently equivalent. Signed-off-by: Miklos Szeredi diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 7299e96..ab1e3dc 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -450,10 +450,10 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, /* * Need to check if we started out being a lower dir, but got copied up */ - if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) { + if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) { struct inode *inode = file_inode(file); - realfile =lockless_dereference(od->upperfile); + realfile = lockless_dereference(od->upperfile); if (!realfile) { struct path upperpath; -- cgit v0.10.2 From 01a4cc4d0cd6a836c7b923760e8eb1cbb6a47258 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Thu, 20 Nov 2014 11:17:33 +0100 Subject: bnx2fc: do not add shared skbs to the fcoe_rx_list In some cases, the fcoe_rx_list may contains multiple instances of the same skb (the so called "shared skbs"). the bnx2fc_l2_rcv thread is a loop that extracts a skb from the list, modifies (and destroys) its content and then proceed to the next one. The problem is that if the skb is shared, the remaining instances will be corrupted. The solution is to use skb_share_check() before adding the skb to the fcoe_rx_list. [ 6286.808725] ------------[ cut here ]------------ [ 6286.808729] WARNING: at include/scsi/fc_frame.h:173 bnx2fc_l2_rcv_thread+0x425/0x450 [bnx2fc]() [ 6286.808748] Modules linked in: bnx2x(-) mdio dm_service_time bnx2fc cnic uio fcoe libfcoe 8021q garp stp mrp libfc llc scsi_transport_fc scsi_tgt sg iTCO_wdt iTCO_vendor_support coretemp kvm_intel kvm crct10dif_pclmul crc32_pclmul crc32c_intel e1000e ghash_clmulni_intel aesni_intel lrw gf128mul glue_helper ablk_helper ptp cryptd hpilo serio_raw hpwdt lpc_ich pps_core ipmi_si pcspkr mfd_core ipmi_msghandler shpchp pcc_cpufreq mperf nfsd auth_rpcgss nfs_acl lockd sunrpc dm_multipath xfs libcrc32c ata_generic pata_acpi sd_mod crc_t10dif crct10dif_common mgag200 syscopyarea sysfillrect sysimgblt i2c_algo_bit ata_piix drm_kms_helper ttm drm libata i2c_core hpsa dm_mirror dm_region_hash dm_log dm_mod [last unloaded: mdio] [ 6286.808750] CPU: 3 PID: 1304 Comm: bnx2fc_l2_threa Not tainted 3.10.0-121.el7.x86_64 #1 [ 6286.808750] Hardware name: HP ProLiant DL120 G7, BIOS J01 07/01/2013 [ 6286.808752] 0000000000000000 000000000b36e715 ffff8800deba1e00 ffffffff815ec0ba [ 6286.808753] ffff8800deba1e38 ffffffff8105dee1 ffffffffa05618c0 ffff8801e4c81888 [ 6286.808754] ffffe8ffff663868 ffff8801f402b180 ffff8801f56bc000 ffff8800deba1e48 [ 6286.808754] Call Trace: [ 6286.808759] [] dump_stack+0x19/0x1b [ 6286.808762] [] warn_slowpath_common+0x61/0x80 [ 6286.808763] [] warn_slowpath_null+0x1a/0x20 [ 6286.808765] [] bnx2fc_l2_rcv_thread+0x425/0x450 [bnx2fc] [ 6286.808767] [] ? bnx2fc_disable+0x90/0x90 [bnx2fc] [ 6286.808769] [] kthread+0xcf/0xe0 [ 6286.808770] [] ? kthread_create_on_node+0x140/0x140 [ 6286.808772] [] ret_from_fork+0x7c/0xb0 [ 6286.808773] [] ? kthread_create_on_node+0x140/0x140 [ 6286.808774] ---[ end trace c6cdb939184ccb4e ]--- Signed-off-by: Maurizio Lombardi Acked-by: Chad Dupuis Signed-off-by: Christoph Hellwig Cc: stable@vger.kernel.org diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 79e5c94..72533c5 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -412,6 +412,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, struct fc_frame_header *fh; struct fcoe_rcv_info *fr; struct fcoe_percpu_s *bg; + struct sk_buff *tmp_skb; unsigned short oxid; interface = container_of(ptype, struct bnx2fc_interface, @@ -424,6 +425,12 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, goto err; } + tmp_skb = skb_share_check(skb, GFP_ATOMIC); + if (!tmp_skb) + goto err; + + skb = tmp_skb; + if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n"); goto err; -- cgit v0.10.2 From 1899045510ff109980d9cc34e330fd8ca3631871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20S=C3=BCnkenberg?= Date: Tue, 18 Nov 2014 20:23:32 +0100 Subject: scsi: add Intel Multi-Flex to scsi scan blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intel Multi-Flex LUNs choke on REPORT SUPPORTED OPERATION CODES resulting in sd_mod hanging for several minutes on startup. The issue was introduced with WRITE SAME discovery heuristics. Fixes: 5db44863b6eb ("[SCSI] sd: Implement support for WRITE SAME") Signed-off-by: Christian Sünkenberg Signed-off-by: Christoph Hellwig Cc: stable@vger.kernel.org diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 49014a1..c1d04d4 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -202,6 +202,7 @@ static struct { {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, {"INSITE", "I325VM", NULL, BLIST_KEY}, + {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC}, {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, -- cgit v0.10.2 From 7c48bfd038e570cd12b33d6ab13316f3a0b0d1a7 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 23 Oct 2014 13:25:12 +0300 Subject: ufs: fix reference counting of W-LUs UFS driver adds three well known LUs in the initialization, but those reference counts are not decremented, so it makes ufshcd module impossible to unload. This fixes it by putting scsi_device_put() in the initalization, and in order to protect concurrent access to hba->sdev_ufs_device (UFS Device W-LU) from manual delete, increment the reference count while requesting device power mode setting. The rest of W-LUs (hba->sdev_boot and hba->sdev_rpmb) are not directly used from driver, so these references in struct ufs_hba are removed. Signed-off-by: Akinobu Mita Reviewed-by: Maya Erez Reviewed-by: Dolev Raviv Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 497c38a..59b65446 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2844,8 +2844,13 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) hba = shost_priv(sdev->host); scsi_deactivate_tcq(sdev, hba->nutrs); /* Drop the reference as it won't be needed anymore */ - if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) + if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) { + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); hba->sdev_ufs_device = NULL; + spin_unlock_irqrestore(hba->host->host_lock, flags); + } } /** @@ -4062,6 +4067,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) { int ret = 0; + struct scsi_device *sdev_rpmb; + struct scsi_device *sdev_boot; hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL); @@ -4070,26 +4077,27 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) hba->sdev_ufs_device = NULL; goto out; } + scsi_device_put(hba->sdev_ufs_device); - hba->sdev_boot = __scsi_add_device(hba->host, 0, 0, + sdev_boot = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL); - if (IS_ERR(hba->sdev_boot)) { - ret = PTR_ERR(hba->sdev_boot); - hba->sdev_boot = NULL; + if (IS_ERR(sdev_boot)) { + ret = PTR_ERR(sdev_boot); goto remove_sdev_ufs_device; } + scsi_device_put(sdev_boot); - hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0, + sdev_rpmb = __scsi_add_device(hba->host, 0, 0, ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL); - if (IS_ERR(hba->sdev_rpmb)) { - ret = PTR_ERR(hba->sdev_rpmb); - hba->sdev_rpmb = NULL; + if (IS_ERR(sdev_rpmb)) { + ret = PTR_ERR(sdev_rpmb); goto remove_sdev_boot; } + scsi_device_put(sdev_rpmb); goto out; remove_sdev_boot: - scsi_remove_device(hba->sdev_boot); + scsi_remove_device(sdev_boot); remove_sdev_ufs_device: scsi_remove_device(hba->sdev_ufs_device); out: @@ -4097,30 +4105,6 @@ out: } /** - * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by - * ufshcd_scsi_add_wlus() - * @hba: per-adapter instance - * - */ -static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba) -{ - if (hba->sdev_ufs_device) { - scsi_remove_device(hba->sdev_ufs_device); - hba->sdev_ufs_device = NULL; - } - - if (hba->sdev_boot) { - scsi_remove_device(hba->sdev_boot); - hba->sdev_boot = NULL; - } - - if (hba->sdev_rpmb) { - scsi_remove_device(hba->sdev_rpmb); - hba->sdev_rpmb = NULL; - } -} - -/** * ufshcd_probe_hba - probe hba to detect device and initialize * @hba: per-adapter instance * @@ -4675,11 +4659,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, { unsigned char cmd[6] = { START_STOP }; struct scsi_sense_hdr sshdr; - struct scsi_device *sdp = hba->sdev_ufs_device; + struct scsi_device *sdp; + unsigned long flags; int ret; - if (!sdp || !scsi_device_online(sdp)) - return -ENODEV; + spin_lock_irqsave(hba->host->host_lock, flags); + sdp = hba->sdev_ufs_device; + if (sdp) { + ret = scsi_device_get(sdp); + if (!ret && !scsi_device_online(sdp)) { + ret = -ENODEV; + scsi_device_put(sdp); + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + + if (ret) + return ret; /* * If scsi commands fail, the scsi mid-layer schedules scsi error- @@ -4718,6 +4716,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, if (!ret) hba->curr_dev_pwr_mode = pwr_mode; out: + scsi_device_put(sdp); hba->host->eh_noresume = 0; return ret; } @@ -5231,7 +5230,6 @@ EXPORT_SYMBOL(ufshcd_shutdown); void ufshcd_remove(struct ufs_hba *hba) { scsi_remove_host(hba->host); - ufshcd_scsi_remove_wlus(hba); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); ufshcd_hba_stop(hba); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 58ecdff..4a574aa 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -392,8 +392,6 @@ struct ufs_hba { * "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; - struct scsi_device *sdev_rpmb; - struct scsi_device *sdev_boot; enum ufs_dev_pwr_mode curr_dev_pwr_mode; enum uic_link_state uic_link_state; -- cgit v0.10.2 From 5064636c759628caee3b855048be25a9a33cc8ad Mon Sep 17 00:00:00 2001 From: Yaniv Gardi Date: Thu, 23 Oct 2014 13:25:13 +0300 Subject: ufs: fix power info after link start-up After link start-up power mode will always be PWM G1. This is not reflected in the pwr_info struct which will keep the previous values. Since ufshcd_change_power_mode() tries to avoid unnecessary power mode change if the requested power mode and current power mode are same, power mode change won't execute again after driver initialization. This patch solves the problem by setting pwr_info to PWM G1 after link start-up. Signed-off-by: Yaniv Gardi Signed-off-by: Dolev Raviv Reviewed-by: Maya Erez Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 59b65446..77a4e38 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2246,6 +2246,22 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) return ret; } + /** + * ufshcd_init_pwr_info - setting the POR (power on reset) + * values in hba power info + * @hba: per-adapter instance + */ +static void ufshcd_init_pwr_info(struct ufs_hba *hba) +{ + hba->pwr_info.gear_rx = UFS_PWM_G1; + hba->pwr_info.gear_tx = UFS_PWM_G1; + hba->pwr_info.lane_rx = 1; + hba->pwr_info.lane_tx = 1; + hba->pwr_info.pwr_rx = SLOWAUTO_MODE; + hba->pwr_info.pwr_tx = SLOWAUTO_MODE; + hba->pwr_info.hs_rate = 0; +} + /** * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device * @hba: per-adapter instance @@ -4118,6 +4134,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ret) goto out; + ufshcd_init_pwr_info(hba); + /* UniPro link is active now */ ufshcd_set_link_active(hba); -- cgit v0.10.2 From b7bc596ebbe0cddc97d76ef9309f64471bbf13eb Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 19 Nov 2014 13:12:54 -0500 Subject: drm/radeon: disable native backlight control on pre-r6xx asics (v2) Just use the acpi interface. That's what windows uses on this generation and it's the only thing that seems to work reliably on these generation parts. You can still force the native backlight interface by setting radeon.backlight=1 Bug: https://bugzilla.kernel.org/show_bug.cgi?id=88501 v2: merge into above if/else block Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 9a19e52..6b670b0 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -179,6 +179,9 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder, (rdev->pdev->subsystem_vendor == 0x1734) && (rdev->pdev->subsystem_device == 0x1107)) use_bl = false; + /* disable native backlight control on older asics */ + else if (rdev->family < CHIP_R600) + use_bl = false; else use_bl = true; } -- cgit v0.10.2 From 28731d5818ae25b92d1fb82fe0ac196e97102c1b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 12 Nov 2014 19:17:02 -0500 Subject: drm/radeon: fix endian swapping in vbios fetch for tdp table Value needs to be swapped on BE. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index f6309bd..b5c73df 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -1256,7 +1256,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) (mode_info->atom_context->bios + data_offset + le16_to_cpu(ext_hdr->usPowerTuneTableOffset)); rdev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit = - ppt->usMaximumPowerDeliveryLimit; + le16_to_cpu(ppt->usMaximumPowerDeliveryLimit); pt = &ppt->power_tune_table; } else { ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *) -- cgit v0.10.2 From 233b594bdf6d846f03816eec1c19ccbd7e4618c0 Mon Sep 17 00:00:00 2001 From: Dolev Raviv Date: Thu, 23 Oct 2014 13:25:14 +0300 Subject: scsi: ufs: fix static checker errors in ufshcd_system_suspend This patch fixes newly introduced sparse warning in ufshcd_system_suspend, introduced by UFS power management series. Sparse warning: drivers/scsi/ufs/ufshcd.c:5118 ufshcd_system_suspend() error: we previously assumed 'hba' could be null (see line 5089) To fix it, we return 0 in case HBA is not initialized or is not powered. Signed-off-by: Dolev Raviv Reviewed-by: Maya Erez Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 77a4e38..d3f6ddb 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5104,7 +5104,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba) int ret = 0; if (!hba || !hba->is_powered) - goto out; + return 0; if (pm_runtime_suspended(hba->dev)) { if (hba->rpm_lvl == hba->spm_lvl) -- cgit v0.10.2 From 758581b929ce50ae3a5cf80735e3e58ef45c31b5 Mon Sep 17 00:00:00 2001 From: Dolev Raviv Date: Thu, 23 Oct 2014 13:25:15 +0300 Subject: scsi: ufs: fix static checker warning in ufshcd_populate_vreg This patch fixes newly introduced static checker warning in ufshcd_populate_vreg, introduced by UFS power management series. Warning: drivers/scsi/ufs/ufshcd-pltfrm.c:167 ufshcd_populate_vreg() warn: missing error code here? 'devm_kzalloc()' failed. 'ret' = '0' To fix it we return -ENOMEM and skip the message print. Signed-off-by: Dolev Raviv Reviewed-by: Maya Erez Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 8adf067..2cdec78 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -162,10 +162,8 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name, } vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); - if (!vreg) { - dev_err(dev, "No memory for %s regulator\n", name); - goto out; - } + if (!vreg) + return -ENOMEM; vreg->name = kstrdup(name, GFP_KERNEL); -- cgit v0.10.2 From eda910e4d03af878482055ce511a522d9bbda0e8 Mon Sep 17 00:00:00 2001 From: Dolev Raviv Date: Thu, 23 Oct 2014 13:25:16 +0300 Subject: scsi: ufs: fix static checker warning in __ufshcd_setup_clocks This patch fixes newly introduced static checker warning in __ufshcd_setup_clocks, introduced by UFS power management series. Warning: drivers/scsi/ufs/ufshcd.c:4474 __ufshcd_setup_clocks() warn: we tested 'ret' before and it was 'false' To fix it we remove the (!ret) from the condition. Signed-off-by: Dolev Raviv Reviewed-by: Maya Erez Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d3f6ddb..b9da446 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4473,7 +4473,7 @@ out: if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled) clk_disable_unprepare(clki->clk); } - } else if (!ret && on) { + } else if (on) { spin_lock_irqsave(hba->host->host_lock, flags); hba->clk_gating.state = CLKS_ON; spin_unlock_irqrestore(hba->host->host_lock, flags); -- cgit v0.10.2 From e8cb64db81e8c88c5c824ca74c2e57b4c6919ca6 Mon Sep 17 00:00:00 2001 From: Dolev Raviv Date: Thu, 23 Oct 2014 13:25:17 +0300 Subject: scsi: ufs: fix static checker warning in ufshcd_parse_clock_info This patch fixes newly introduced static checker warning in ufshcd_parse_clock_info, introduced by UFS power management series. Warning: drivers/scsi/ufs/ufshcd-pltfrm.c:138 ufshcd_parse_clock_info() warn: passing devm_ allocated variable to kfree. 'clkfreq' To fix it we remove the kfree(clkfreq) statement. In addition we removed the redundant goto label. Signed-off-by: Dolev Raviv Reviewed-by: Maya Erez Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 2cdec78..1c3467b 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -102,7 +102,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq), GFP_KERNEL); if (!clkfreq) { - dev_err(dev, "%s: no memory\n", "freq-table-hz"); ret = -ENOMEM; goto out; } @@ -112,19 +111,19 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) if (ret && (ret != -EINVAL)) { dev_err(dev, "%s: error reading array %d\n", "freq-table-hz", ret); - goto free_clkfreq; + return ret; } for (i = 0; i < sz; i += 2) { ret = of_property_read_string_index(np, "clock-names", i/2, (const char **)&name); if (ret) - goto free_clkfreq; + goto out; clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); if (!clki) { ret = -ENOMEM; - goto free_clkfreq; + goto out; } clki->min_freq = clkfreq[i]; @@ -134,8 +133,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) clki->min_freq, clki->max_freq, clki->name); list_add_tail(&clki->list, &hba->clk_list_head); } -free_clkfreq: - kfree(clkfreq); out: return ret; } -- cgit v0.10.2 From a1d69c60c44134f64945bbf6a6dfda22eaf4a214 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Nov 2014 22:13:10 +0100 Subject: brcmfmac: don't include linux/unaligned/access_ok.h This is a specific implementation, is the multiplexer that has the arch-specific knowledge of which of the implementations needs to be used, so include that. This issue was revealed by kbuild testing when was added in resulting in redefinition of get_unaligned_be16 (and probably others). Cc: stable@vger.kernel.org # v3.17 Reported-by: Fengguang Wu Signed-off-by: Johannes Berg Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 8c0632e..16fef33 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -19,10 +19,10 @@ #include #include #include -#include #include #include #include +#include #include #include -- cgit v0.10.2 From 5aeee3424fa5926a53885b9defb593971e446d77 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 11:02:14 +0100 Subject: ALSA: usb-audio: Refactor ignore_ctl_error checks Introduce an internal helper macro for avoiding many open codes. The only slight behavior change is in a couple of get ballcks where the value is reset at error no matter whether ignore_ctl_error is set or not. Actually this is even safer than before. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c6209ce..a4fbfff 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -136,6 +136,10 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) return strlcpy(buf, p->name, buflen); } +/* ignore the error value if ignore_ctl_error flag is set */ +#define filter_error(cval, err) \ + ((cval)->mixer->ignore_ctl_error ? 0 : (err)) + /* check whether the control should be ignored */ static inline int check_ignored_ctl(const struct usbmix_name_map *p) @@ -1088,7 +1092,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, continue; err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[cnt] = val; cnt++; @@ -1098,7 +1102,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, /* master channel */ err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } @@ -1120,7 +1124,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, continue; err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { @@ -1133,7 +1137,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, /* master channel */ err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -1628,12 +1632,10 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, int err, val; err = get_cur_ctl_value(cval, cval->control << 8, &val); - if (err < 0 && cval->mixer->ignore_ctl_error) { + if (err < 0) { ucontrol->value.integer.value[0] = cval->min; - return 0; + return filter_error(cval, err); } - if (err < 0) - return err; val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; return 0; @@ -1647,11 +1649,8 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, int val, oval, err; err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -1923,11 +1922,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, err = get_cur_ctl_value(cval, cval->control << 8, &val); if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.enumerated.item[0] = 0; - return 0; - } - return err; + ucontrol->value.enumerated.item[0] = 0; + return filter_error(cval, err); } val = get_relative_value(cval, val); ucontrol->value.enumerated.item[0] = val; @@ -1942,11 +1938,8 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, int val, oval, err; err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.enumerated.item[0]; val = get_abs_value(cval, val); if (val != oval) { -- cgit v0.10.2 From b5e212a3051b65e426a513901d9c7001681c7215 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Wed, 19 Nov 2014 13:56:19 -0800 Subject: x86, syscall: Fix _TIF_NOHZ handling in syscall_trace_enter_phase1 TIF_NOHZ is 19 (i.e. _TIF_SYSCALL_TRACE | _TIF_NOTIFY_RESUME | _TIF_SINGLESTEP), not (1<<19). This code is involved in Dave's trinity lockup, but I don't see why it would cause any of the problems he's seeing, except inadvertently by causing a different path through entry_64.S's syscall handling. Signed-off-by: Andy Lutomirski Cc: Don Zickus Cc: Peter Zijlstra Cc: Dave Jones Cc: Linus Torvalds Link: http://lkml.kernel.org/r/a6cd3b60a3f53afb6e1c8081b0ec30ff19003dd7.1416434075.git.luto@amacapital.net Signed-off-by: Thomas Gleixner diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 749b0e4..e510618 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1484,7 +1484,7 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) */ if (work & _TIF_NOHZ) { user_exit(); - work &= ~TIF_NOHZ; + work &= ~_TIF_NOHZ; } #ifdef CONFIG_SECCOMP -- cgit v0.10.2 From 2dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76b Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Fri, 7 Nov 2014 19:12:29 +0530 Subject: thermal: cpu_cooling: Update always cpufreq policy with thermal constraints Existing code updates cupfreq policy only while executing cpufreq_apply_cooling() function (i.e. when notify_device != NOTIFY_INVALID). It doesn't apply constraints when cpufreq policy update happens from any other place but it should update the cpufreq policy with thermal constraints every time when there is a cpufreq policy update, to keep state of cpufreq_cooling_device and max_feq of cpufreq policy in sync. For instance while resuming cpufreq updates cpufreq_policy and it restores default policy->usr_policy values irrespective of cooling device's cpufreq_state since notification gets missed because (notify_device == NOTIFY_INVALID). Another problem, is that userspace is able to change max_freq irrespective of cooling device's state, as notification gets missed. This patch modifies code to maintain a global cpufreq_dev_list and applies constraints of all matching cooling devices for policy's cpu when there is any policy update(ends up applying the lowest max_freq among the matching cpu cooling devices). This patch also removes redundant check (max_freq > policy->user_policy.max), as cpufreq framework takes care of user_policy constraints already where ever required, otherwise its causing an issue while increasing max_freq in normal scenerio as it restores max_freq with policy->user_policy.max which is old (smaller) value. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 1ab0018..ad09e51 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -50,15 +50,14 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int cpufreq_val; struct cpumask allowed_cpus; + struct list_head node; }; static DEFINE_IDR(cpufreq_idr); static DEFINE_MUTEX(cooling_cpufreq_lock); static unsigned int cpufreq_dev_count; -/* notify_table passes value to the CPUFREQ_ADJUST callback function. */ -#define NOTIFY_INVALID NULL -static struct cpufreq_cooling_device *notify_device; +static LIST_HEAD(cpufreq_dev_list); /** * get_idr - function to get a unique id. @@ -287,15 +286,12 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, cpufreq_device->cpufreq_state = cooling_state; cpufreq_device->cpufreq_val = clip_freq; - notify_device = cpufreq_device; for_each_cpu(cpuid, mask) { if (is_cpufreq_valid(cpuid)) cpufreq_update_policy(cpuid); } - notify_device = NOTIFY_INVALID; - return 0; } @@ -316,21 +312,28 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, { struct cpufreq_policy *policy = data; unsigned long max_freq = 0; + struct cpufreq_cooling_device *cpufreq_dev; - if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID) + if (event != CPUFREQ_ADJUST) return 0; - if (cpumask_test_cpu(policy->cpu, ¬ify_device->allowed_cpus)) - max_freq = notify_device->cpufreq_val; - else - return 0; + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { + if (!cpumask_test_cpu(policy->cpu, + &cpufreq_dev->allowed_cpus)) + continue; + + if (!cpufreq_dev->cpufreq_val) + cpufreq_dev->cpufreq_val = get_cpu_frequency( + cpumask_any(&cpufreq_dev->allowed_cpus), + cpufreq_dev->cpufreq_state); - /* Never exceed user_policy.max */ - if (max_freq > policy->user_policy.max) - max_freq = policy->user_policy.max; + max_freq = cpufreq_dev->cpufreq_val; - if (policy->max != max_freq) - cpufreq_verify_within_limits(policy, 0, max_freq); + if (policy->max != max_freq) + cpufreq_verify_within_limits(policy, 0, max_freq); + } + mutex_unlock(&cooling_cpufreq_lock); return 0; } @@ -486,6 +489,7 @@ __cpufreq_cooling_register(struct device_node *np, cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); cpufreq_dev_count++; + list_add(&cpufreq_dev->node, &cpufreq_dev_list); mutex_unlock(&cooling_cpufreq_lock); @@ -549,6 +553,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) cpufreq_dev = cdev->devdata; mutex_lock(&cooling_cpufreq_lock); + list_del(&cpufreq_dev->node); cpufreq_dev_count--; /* Unregister the notifier for the last cpufreq cooling device */ -- cgit v0.10.2 From a5f6fc28d6e6cc379c6839f21820e62262419584 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Wed, 19 Nov 2014 18:05:26 +0100 Subject: pptp: fix stack info leak in pptp_getname() pptp_getname() only partially initializes the stack variable sa, particularly only fills the pptp part of the sa_addr union. The code thereby discloses 16 bytes of kernel stack memory via getsockname(). Fix this by memset(0)'ing the union before. Cc: Dmitry Kozlov Signed-off-by: Mathias Krause Signed-off-by: David S. Miller diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 1aff970..1dc628f 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -506,7 +506,9 @@ static int pptp_getname(struct socket *sock, struct sockaddr *uaddr, int len = sizeof(struct sockaddr_pppox); struct sockaddr_pppox sp; - sp.sa_family = AF_PPPOX; + memset(&sp.sa_addr, 0, sizeof(sp.sa_addr)); + + sp.sa_family = AF_PPPOX; sp.sa_protocol = PX_PROTO_PPTP; sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr; -- cgit v0.10.2 From d3052bb5d306b29c1e7d9e5998c5ac4ca1ff0ca9 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Wed, 19 Nov 2014 13:54:49 -0800 Subject: openvswitch: Don't validate IPv6 label masks. When userspace doesn't provide a mask, OVS datapath generates a fully unwildcarded mask for the flow by copying the flow and setting all bits in all fields. For IPv6 label, this creates a mask that matches on the upper 12 bits, causing the following error: openvswitch: netlink: Invalid IPv6 flow label value (value=ffffffff, max=fffff) This patch ignores the label validation check for masks, avoiding this error. Signed-off-by: Joe Stringer Acked-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index fa4ec2e..089b195 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -690,7 +690,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, return -EINVAL; } - if (ipv6_key->ipv6_label & htonl(0xFFF00000)) { + if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) { OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n", ntohl(ipv6_key->ipv6_label), (1 << 20) - 1); return -EINVAL; -- cgit v0.10.2 From 01462405f0c093b2f8dfddafcadcda6c9e4c5cdf Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Wed, 19 Nov 2014 23:05:49 +0100 Subject: ipx: fix locking regression in ipx_sendmsg and ipx_recvmsg This fixes an old regression introduced by commit b0d0d915 (ipx: remove the BKL). When a recvmsg syscall blocks waiting for new data, no data can be sent on the same socket with sendmsg because ipx_recvmsg() sleeps with the socket locked. This breaks mars-nwe (NetWare emulator): - the ncpserv process reads the request using recvmsg - ncpserv forks and spawns nwconn - ncpserv calls a (blocking) recvmsg and waits for new requests - nwconn deadlocks in sendmsg on the same socket Commit b0d0d915 has simply replaced BKL locking with lock_sock/release_sock. Unlike now, BKL got unlocked while sleeping, so a blocking recvmsg did not block a concurrent sendmsg. Only keep the socket locked while actually working with the socket data and release it prior to calling skb_recv_datagram(). Signed-off-by: Jiri Bohac Reviewed-by: Arnd Bergmann Signed-off-by: David S. Miller diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 91729b8..1b095ca 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1764,6 +1764,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, struct ipxhdr *ipx = NULL; struct sk_buff *skb; int copied, rc; + bool locked = true; lock_sock(sk); /* put the autobinding in */ @@ -1790,6 +1791,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, if (sock_flag(sk, SOCK_ZAPPED)) goto out; + release_sock(sk); + locked = false; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &rc); if (!skb) { @@ -1826,7 +1829,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, out_free: skb_free_datagram(sk, skb); out: - release_sock(sk); + if (locked) + release_sock(sk); return rc; } -- cgit v0.10.2 From 17544e2ad78fa0bbff6fcdbf09426d04ce95ed1e Mon Sep 17 00:00:00 2001 From: Anish Bhatt Date: Thu, 20 Nov 2014 17:11:46 -0800 Subject: cxgb4 : Fix DCB priority groups being returned in wrong order Peer priority groups were being reversed, but this was missed in the previous fix sent out for this issue. v2 : Previous patch was doing extra unnecessary work, result is the same. Please ignore previous patch Fixes : ee7bc3cdc270 ('cxgb4 : dcb open-lldp interop fixes') Signed-off-by: Anish Bhatt Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c index cca6049..4fe3360 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c @@ -1082,7 +1082,7 @@ static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg) pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); for (i = 0; i < CXGB4_MAX_PRIORITY; i++) - pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF; + pg->prio_pg[7 - i] = (pgid >> (i * 4)) & 0xF; INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; -- cgit v0.10.2 From 892d6eb1245b771987afb8667a65344e568d3439 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 20 Nov 2014 17:03:05 +0800 Subject: virtio-net: validate features during probe We currently trigger BUG when VIRTIO_NET_F_CTRL_VQ is not set but one of features depending on it is. That's not a friendly way to report errors to hypervisors. Let's check, and fail probe instead. Cc: Rusty Russell Cc: Cornelia Huck Cc: Wanlong Gao Signed-off-by: Michael S. Tsirkin Signed-off-by: Jason Wang Acked-by: Cornelia Huck Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ec2a8b4..b0bc8ea 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1673,6 +1673,40 @@ static const struct attribute_group virtio_net_mrg_rx_group = { }; #endif +static bool virtnet_fail_on_feature(struct virtio_device *vdev, + unsigned int fbit, + const char *fname, const char *dname) +{ + if (!virtio_has_feature(vdev, fbit)) + return false; + + dev_err(&vdev->dev, "device advertises feature %s but not %s", + fname, dname); + + return true; +} + +#define VIRTNET_FAIL_ON(vdev, fbit, dbit) \ + virtnet_fail_on_feature(vdev, fbit, #fbit, dbit) + +static bool virtnet_validate_features(struct virtio_device *vdev) +{ + if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ) && + (VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_RX, + "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_VLAN, + "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE, + "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_MQ, "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR, + "VIRTIO_NET_F_CTRL_VQ"))) { + return false; + } + + return true; +} + static int virtnet_probe(struct virtio_device *vdev) { int i, err; @@ -1680,6 +1714,9 @@ static int virtnet_probe(struct virtio_device *vdev) struct virtnet_info *vi; u16 max_queue_pairs; + if (!virtnet_validate_features(vdev)) + return -EINVAL; + /* Find if host supports multiqueue virtio_net device */ err = virtio_cread_feature(vdev, VIRTIO_NET_F_MQ, struct virtio_net_config, -- cgit v0.10.2 From eaa8e5ef18fa9e09286482a4ded3a3cad36e44b2 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 21 Nov 2014 15:49:11 +0800 Subject: ALSA: hda/realtek - Supported HP mute Led for ALC286 New HP machine supported output mute led and input mute led. ALC286: GPIO1 to control output mute led. GPIO5 to control input mute led. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3c29a55..3fcb7d9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3299,6 +3299,27 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, } } +static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_verb gpio_init[] = { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x22 }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x22 }, + {} + }; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; + spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x02; + spec->gpio_mic_led_mask = 0x20; + snd_hda_add_verbs(codec, gpio_init); + } +} + /* turn on/off mic-mute LED per capture hook */ static void alc269_fixup_hp_cap_mic_mute_hook(struct hda_codec *codec, struct snd_kcontrol *kcontrol, @@ -4238,6 +4259,7 @@ enum { ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, ALC280_FIXUP_HP_GPIO4, + ALC286_FIXUP_HP_GPIO_LED, }; static const struct hda_fixup alc269_fixups[] = { @@ -4705,6 +4727,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc280_fixup_hp_gpio4, }, + [ALC286_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc286_fixup_hp_gpio_led, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -4745,6 +4771,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), -- cgit v0.10.2 From 3360b84b8ed1f08bfb39743465b858a04492fcc3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 11:47:04 +0100 Subject: ALSA: usb-audio: Allow quirks to handle own resume and proc dump So far, we blindly assumed that the all usb-audio mixer elements follow the standard and apply the standard resume method for the registered elements in the id_elems[] list. However, some quirks really need the own resume and it's incomplete for now. This patch enhances the resume handling in two folds: - split some fields in struct usb_mixer_elem_info into a smaller header struct (usb_mixer_elem_list) for keeping the minimal information in the linked-list; the usb_mixer_elem_info embeds this header struct instead - add resume and dump callbacks to usb_mixer_elem_list struct to allow quirks providing the own methods For the standard mixer elements, these new callbacks are set to the standard ones as default, thus there is no functional change by this patch yet. The dump and resume callbacks are typedef'ed for ease of later patches using arrays of such function pointers. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index a4fbfff..41650d5 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -138,7 +138,7 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) /* ignore the error value if ignore_ctl_error flag is set */ #define filter_error(cval, err) \ - ((cval)->mixer->ignore_ctl_error ? 0 : (err)) + ((cval)->head.mixer->ignore_ctl_error ? 0 : (err)) /* check whether the control should be ignored */ static inline int @@ -290,13 +290,13 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; int idx = 0, err; - err = snd_usb_autoresume(cval->mixer->chip); + err = snd_usb_autoresume(chip); if (err < 0) return -EIO; @@ -304,7 +304,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, val_len) >= val_len) { @@ -320,14 +320,14 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, out: up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(cval->mixer->chip); + snd_usb_autosuspend(chip); return err; } static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */ unsigned char *val; int idx = 0, ret, size; @@ -351,7 +351,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, if (chip->shutdown) { ret = -ENODEV; } else { - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, size); @@ -396,7 +396,7 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, { validx += cval->idx_off; - return (cval->mixer->protocol == UAC_VERSION_1) ? + return (cval->head.mixer->protocol == UAC_VERSION_1) ? get_ctl_value_v1(cval, request, validx, value_ret) : get_ctl_value_v2(cval, request, validx, value_ret); } @@ -427,8 +427,8 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, } err = get_cur_mix_raw(cval, channel, value); if (err < 0) { - if (!cval->mixer->ignore_ctl_error) - usb_audio_dbg(cval->mixer->chip, + if (!cval->head.mixer->ignore_ctl_error) + usb_audio_dbg(cval->head.mixer->chip, "cannot get current value for control %d ch %d: err = %d\n", cval->control, channel, err); return err; @@ -445,13 +445,13 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int idx = 0, val_len, err, timeout = 10; validx += cval->idx_off; - if (cval->mixer->protocol == UAC_VERSION_1) { + if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ /* audio class v2 controls are always 2 bytes in size */ @@ -476,7 +476,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, @@ -510,7 +510,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, cval->ch_readonly & (1 << (channel - 1)); if (read_only) { - usb_audio_dbg(cval->mixer->chip, + usb_audio_dbg(cval->head.mixer->chip, "%s(): channel %d of control %d is read_only\n", __func__, channel, cval->control); return 0; @@ -569,10 +569,10 @@ static int check_matrix_bitmap(unsigned char *bmap, * if failed, give up and free the control instance. */ -int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl) { - struct usb_mixer_elem_info *cval = kctl->private_data; + struct usb_mixer_interface *mixer = list->mixer; int err; while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) @@ -582,9 +582,9 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, err); return err; } - cval->elem_id = &kctl->id; - cval->next_id_elem = mixer->id_elems[cval->id]; - mixer->id_elems[cval->id] = cval; + list->kctl = kctl; + list->next_id_elem = mixer->id_elems[list->id]; + mixer->id_elems[list->id] = list; return 0; } @@ -833,7 +833,7 @@ void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; switch (chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ @@ -958,10 +958,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { - usb_audio_err(cval->mixer->chip, + usb_audio_err(cval->head.mixer->chip, "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->id, snd_usb_ctrl_intf(cval->mixer->chip), - cval->control, cval->id); + cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip), + cval->control, cval->head.id); return -EINVAL; } if (get_ctl_value(cval, UAC_GET_RES, @@ -1065,7 +1065,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, kcontrol->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); - snd_ctl_notify(cval->mixer->chip->card, + snd_ctl_notify(cval->head.mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id); } @@ -1235,8 +1235,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = control; cval->cmask = ctl_mask; cval->val_type = audio_feature_info[control-1].type; @@ -1347,14 +1346,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, range); usb_audio_warn(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); } usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); - snd_usb_mixer_add_control(state->mixer, kctl); + snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -1528,8 +1527,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, if (!cval) return; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { @@ -1561,8 +1559,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state, append_ctl_name(kctl, " Volume"); usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - snd_usb_mixer_add_control(state->mixer, kctl); + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); + snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -1812,8 +1810,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = valinfo->control; cval->val_type = valinfo->val_type; cval->channels = 1; @@ -1866,10 +1863,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, usb_audio_dbg(state->chip, "[%d] PU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); - err = snd_usb_mixer_add_control(state->mixer, kctl); + err = snd_usb_mixer_add_control(&cval->head, kctl); if (err < 0) return err; } @@ -2016,8 +2013,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->val_type = USB_MIXER_U8; cval->channels = 1; cval->min = 1; @@ -2088,11 +2084,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, } usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", - cval->id, kctl->id.name, desc->bNrInPins); - if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0) - return err; - - return 0; + cval->head.id, kctl->id.name, desc->bNrInPins); + return snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -2237,25 +2230,21 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list; - for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &list->kctl->id); } static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, - int unitid, - struct usb_mixer_elem_info *cval) + struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; - snd_iprintf(buffer, " Unit: %i\n", unitid); - if (cval->elem_id) - snd_iprintf(buffer, " Control: name=\"%s\", index=%i\n", - cval->elem_id->name, cval->elem_id->index); snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " - "channels=%i, type=\"%s\"\n", cval->id, + "channels=%i, type=\"%s\"\n", cval->head.id, cval->control, cval->cmask, cval->channels, val_types[cval->val_type]); snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", @@ -2267,7 +2256,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, { struct snd_usb_audio *chip = entry->private_data; struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int unitid; list_for_each_entry(mixer, &chip->mixer_list, list) { @@ -2277,9 +2266,17 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (cval = mixer->id_elems[unitid]; cval; - cval = cval->next_id_elem) - snd_usb_mixer_dump_cval(buffer, unitid, cval); + for (list = mixer->id_elems[unitid]; list; + list = list->next_id_elem) { + snd_iprintf(buffer, " Unit: %i\n", list->id); + if (list->kctl) + snd_iprintf(buffer, + " Control: name=\"%s\", index=%i\n", + list->kctl->id.name, + list->kctl->id.index); + if (list->dump) + list->dump(buffer, list); + } } } } @@ -2287,7 +2284,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, int attribute, int value, int index) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list; __u8 unitid = (index >> 8) & 0xff; __u8 control = (value >> 8) & 0xff; __u8 channel = value & 0xff; @@ -2299,7 +2296,13 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } - for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) { + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + struct usb_mixer_elem_info *info; + + if (!list->kctl) + continue; + + info = (struct usb_mixer_elem_info *)list; if (info->control != control) continue; @@ -2312,7 +2315,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, info->cached = 0; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &info->head.kctl->id); break; case UAC2_CS_RANGE: @@ -2510,8 +2513,9 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) return 0; } -static int restore_mixer_value(struct usb_mixer_elem_info *cval) +static int restore_mixer_value(struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; int c, err, idx; if (cval->cmask) { @@ -2541,19 +2545,19 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) { - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int id, err; - /* FIXME: any mixer quirks? */ - if (reset_resume) { /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { - for (cval = mixer->id_elems[id]; cval; - cval = cval->next_id_elem) { - err = restore_mixer_value(cval); - if (err < 0) - return err; + for (list = mixer->id_elems[id]; list; + list = list->next_id_elem) { + if (list->resume) { + err = list->resume(list); + if (err < 0) + return err; + } } } } @@ -2561,3 +2565,15 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) return snd_usb_mixer_activate(mixer); } #endif + +void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid) +{ + list->mixer = mixer; + list->id = unitid; + list->dump = snd_usb_mixer_dump_cval; +#ifdef CONFIG_PM + list->resume = restore_mixer_value; +#endif +} diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 2478a84..0fe87b7 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -1,6 +1,8 @@ #ifndef __USBMIXER_H #define __USBMIXER_H +#include + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -8,7 +10,7 @@ struct usb_mixer_interface { unsigned int ignore_ctl_error; struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ - struct usb_mixer_elem_info **id_elems; + struct usb_mixer_elem_list **id_elems; /* the usb audio specification version this interface complies to */ int protocol; @@ -36,11 +38,21 @@ enum { USB_MIXER_U16, }; -struct usb_mixer_elem_info { +typedef void (*usb_mixer_elem_dump_func_t)(struct snd_info_buffer *buffer, + struct usb_mixer_elem_list *list); +typedef int (*usb_mixer_elem_resume_func_t)(struct usb_mixer_elem_list *elem); + +struct usb_mixer_elem_list { struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ - struct snd_ctl_elem_id *elem_id; + struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */ + struct snd_kcontrol *kctl; unsigned int id; + usb_mixer_elem_dump_func_t dump; + usb_mixer_elem_resume_func_t resume; +}; + +struct usb_mixer_elem_info { + struct usb_mixer_elem_list head; unsigned int control; /* CS or ICN (high byte) */ unsigned int cmask; /* channel mask bitmap: 0 = master */ unsigned int idx_off; /* Control index offset */ @@ -65,9 +77,13 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set); -int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl); +void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid); + int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 8b55c06..88a408c 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -69,7 +69,6 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, const char *name, snd_kcontrol_tlv_rw_t *tlv_callback) { - int err; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; @@ -77,8 +76,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, if (!cval) return -ENOMEM; - cval->id = unitid; - cval->mixer = mixer; + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->val_type = val_type; cval->channels = 1; cval->control = control; @@ -112,11 +110,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; } /* Add control to mixer */ - err = snd_usb_mixer_add_control(mixer, kctl); - if (err < 0) - return err; - - return 0; + return snd_usb_mixer_add_control(&cval->head, kctl); } static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, @@ -1206,7 +1200,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, int unitid = 12; /* SamleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer->id_elems[unitid]; + cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid]; if (cval) { snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index a0a8745..92dba35 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -436,10 +436,10 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; - struct snd_usb_audio *chip = elem->mixer->chip; + struct snd_usb_audio *chip = elem->head.mixer->chip; unsigned char buf[2 * MAX_CHANNELS] = {0, }; int wValue = (elem->control << 8) | elem->idx_off; - int idx = snd_usb_ctrl_intf(chip) | (elem->id << 8); + int idx = snd_usb_ctrl_intf(chip) | (elem->head.id << 8); int err; err = snd_usb_ctl_msg(chip->dev, @@ -528,10 +528,10 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, if (!elem) return -ENOMEM; - elem->mixer = mixer; + elem->head.mixer = mixer; elem->control = offset; elem->idx_off = num; - elem->id = index; + elem->head.id = index; elem->val_type = val_type; elem->channels = channels; -- cgit v0.10.2 From 9cf3689bfe0784b6f6afb301bece95d3fc3eeb64 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 12:58:51 +0100 Subject: ALSA: usb-audio: Add audigy2nx resume support Rewrite the code to handle LEDs on audigy2nx and co for supporting the proper resume. A new internal helper function add_single_ctl_with_resume() is introduced to manage the usb_mixer_elem_list more easily. Also while we're at it, move audigy2nx_leds[] in usb_mixer_interface struct into the private_value of each kctl, too. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 0fe87b7..0b3740e 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -23,7 +23,6 @@ struct usb_mixer_interface { struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; - u8 audigy2nx_leds[3]; u8 xonar_u1_status; }; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 88a408c..41cacf8 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -144,6 +144,32 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, return 0; } +static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, + int id, + usb_mixer_elem_resume_func_t resume, + const struct snd_kcontrol_new *knew, + struct usb_mixer_elem_list **listp) +{ + struct usb_mixer_elem_list *list; + struct snd_kcontrol *kctl; + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + return -ENOMEM; + if (listp) + *listp = list; + list->mixer = mixer; + list->id = id; + list->resume = resume; + kctl = snd_ctl_new1(knew, list); + if (!kctl) { + kfree(list); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + return snd_usb_mixer_add_control(list, kctl); +} + /* * Sound Blaster remote control configuration * @@ -271,84 +297,90 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - - ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; + ucontrol->value.integer.value[0] = kcontrol->private_value >> 8; return 0; } -static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, + int value, int index) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err; - if (value > 1) - return -EINVAL; - changed = value != mixer->audigy2nx_leds[index]; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) { + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { err = -ENODEV; goto out; } - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + if (chip->usb_id == USB_ID(0x041e, 0x3042)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); /* 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, + if (chip->usb_id == USB_ID(0x041e, 0x30df)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, value, index + 2, NULL, 0); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->audigy2nx_leds[index] = value; - return changed; + up_read(&chip->shutdown_rwsem); + return err; } -static struct snd_kcontrol_new snd_audigy2nx_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "CMSS LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 0, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Power LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 1, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dolby Digital LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 2, - }, +static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + int index = kcontrol->private_value & 0xff; + int value = ucontrol->value.integer.value[0]; + int old_value = kcontrol->private_value >> 8; + int err; + + if (value > 1) + return -EINVAL; + if (value == old_value) + return 0; + kcontrol->private_value = (value << 8) | index; + err = snd_audigy2nx_led_update(mixer, value, index); + return err < 0 ? err : 1; +} + +static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) +{ + int priv_value = list->kctl->private_value; + + return snd_audigy2nx_led_update(list->mixer, priv_value >> 8, + priv_value & 0xff); +} + +/* name and private_value are set dynamically */ +static struct snd_kcontrol_new snd_audigy2nx_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, +}; + +static const char * const snd_audigy2nx_led_names[] = { + "CMSS LED Switch", + "Power LED Switch", + "Dolby Digital LED Switch", }; static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) { int i, err; - for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { + for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) { + struct snd_kcontrol_new knew; + /* USB X-Fi S51 doesn't have a CMSS LED */ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) continue; @@ -361,12 +393,16 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) break; - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); + + knew = snd_audigy2nx_control; + knew.name = snd_audigy2nx_led_names[i]; + knew.private_value = (1 << 8) | i; /* LED on as default */ + err = add_single_ctl_with_resume(mixer, 0, + snd_audigy2nx_led_resume, + &knew, NULL); if (err < 0) return err; } - mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ return 0; } -- cgit v0.10.2 From 5f503ee9e270f8251ba9024bafa8d698050066cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:11:37 +0100 Subject: ALSA: usb-audio: Add Emu0204 channel switch resume support Similar as the previous fix, this adds the proper resume support to Emu0202 "Front Jack Channels" enum mixer element. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 41cacf8..f2b1c0d 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -473,63 +473,71 @@ static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, return 0; } -static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, + int value) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - unsigned int value = ucontrol->value.enumerated.item[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err; unsigned char buf[2]; - if (value > 1) - return -EINVAL; - - buf[0] = 0x01; - buf[1] = value ? 0x02 : 0x01; - - changed = value != kcontrol->private_value; - down_read(&mixer->chip->shutdown_rwsem); + down_read(&chip->shutdown_rwsem); if (mixer->chip->shutdown) { err = -ENODEV; goto out; } - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR, + + buf[0] = 0x01; + buf[1] = value ? 0x02 : 0x01; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 0x0400, 0x0e00, buf, 2); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + unsigned int value = ucontrol->value.enumerated.item[0]; + int err; + + if (value > 1) + return -EINVAL; + + if (value == kcontrol->private_value) + return 0; + kcontrol->private_value = value; - return changed; + err = snd_emu0204_ch_switch_update(mixer, value); + return err < 0 ? err : 1; } +static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_emu0204_ch_switch_update(list->mixer, + list->kctl->private_value); +} -static struct snd_kcontrol_new snd_emu0204_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Front Jack Channels", - .info = snd_emu0204_ch_switch_info, - .get = snd_emu0204_ch_switch_get, - .put = snd_emu0204_ch_switch_put, - .private_value = 0, - }, +static struct snd_kcontrol_new snd_emu0204_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Jack Channels", + .info = snd_emu0204_ch_switch_info, + .get = snd_emu0204_ch_switch_get, + .put = snd_emu0204_ch_switch_put, + .private_value = 0, }; static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) { - int i, err; - - for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_emu0204_controls[i], mixer)); - if (err < 0) - return err; - } - - return 0; + return add_single_ctl_with_resume(mixer, 0, + snd_emu0204_ch_switch_resume, + &snd_emu0204_control, NULL); } + /* ASUS Xonar U1 / U3 controls */ static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, -- cgit v0.10.2 From 2bfb14c3b8fbc787ff4478f9d77ecee78cb922fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:18:15 +0100 Subject: ALSA: usb-audio: Add Xonar U1 resume support This time it's about Xonar U1: add the proper resume support for "Digital Playback Switch" element. Also, the status is moved into kcontrol private_value from usb_mixer_interface struct field. One more cut. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 0b3740e..d3268f0 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -22,8 +22,6 @@ struct usb_mixer_interface { struct urb *rc_urb; struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; - - u8 xonar_u1_status; }; #define MAX_CHANNELS 16 /* max logical channels */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f2b1c0d..4afcf09 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -543,38 +543,52 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); + ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02); return 0; } +static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer, + unsigned char status) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; + else + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 50, 0, &status, 1); + up_read(&chip->shutdown_rwsem); + return err; +} + static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); u8 old_status, new_status; - int err, changed; + int err; - old_status = mixer->xonar_u1_status; + old_status = kcontrol->private_value; if (ucontrol->value.integer.value[0]) new_status = old_status | 0x02; else new_status = old_status & ~0x02; - changed = new_status != old_status; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 50, 0, &new_status, 1); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->xonar_u1_status = new_status; - return changed; + if (new_status == old_status) + return 0; + + kcontrol->private_value = new_status; + err = snd_xonar_u1_switch_update(list->mixer, new_status); + return err < 0 ? err : 1; +} + +static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_xonar_u1_switch_update(list->mixer, + list->kctl->private_value); } static struct snd_kcontrol_new snd_xonar_u1_output_switch = { @@ -583,18 +597,14 @@ static struct snd_kcontrol_new snd_xonar_u1_output_switch = { .info = snd_ctl_boolean_mono_info, .get = snd_xonar_u1_switch_get, .put = snd_xonar_u1_switch_put, + .private_value = 0x05, }; static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) { - int err; - - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); - if (err < 0) - return err; - mixer->xonar_u1_status = 0x05; - return 0; + return add_single_ctl_with_resume(mixer, 0, + snd_xonar_u1_switch_resume, + &snd_xonar_u1_output_switch, NULL); } /* Digidesign Mbox 1 clock source switch (internal/spdif) */ -- cgit v0.10.2 From 25a9a4f91b909822fa07cbc9939c99a8c67d8960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:31:35 +0100 Subject: ALSA: usb-audio: Add Digidesign Mbox 1 resume support Again another quirk fix, just convert to usb_mixer_elem_list with the resume callback for Mbox 1 stuff. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4afcf09..f7ad207 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -616,29 +616,12 @@ static int snd_mbox1_switch_get(struct snd_kcontrol *kctl, return 0; } -static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val) { - struct snd_usb_audio *chip; - struct usb_mixer_interface *mixer; + struct snd_usb_audio *chip = mixer->chip; int err; - bool cur_val, new_val; unsigned char buff[3]; - cur_val = kctl->private_value; - new_val = ucontrol->value.enumerated.item[0]; - - mixer = snd_kcontrol_chip(kctl); - if (snd_BUG_ON(!mixer)) - return -EINVAL; - - chip = mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; - - if (cur_val == new_val) - return 0; - down_read(&chip->shutdown_rwsem); if (chip->shutdown) { err = -ENODEV; @@ -668,7 +651,7 @@ static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, * while S/PDIF sync is enabled and confusing * this configuration. */ - if (new_val == 0) { + if (val == 0) { buff[0] = 0x80; buff[1] = 0xbb; buff[2] = 0x00; @@ -697,10 +680,27 @@ static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3); if (err < 0) goto err; - kctl->private_value = new_val; err: up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + int err; + bool cur_val, new_val; + + cur_val = kctl->private_value; + new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + return 0; + + kctl->private_value = new_val; + err = snd_mbox1_switch_update(mixer, new_val); return err < 0 ? err : 1; } @@ -715,6 +715,11 @@ static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol, return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } +static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_mbox1_switch_update(list->mixer, list->kctl->private_value); +} + static struct snd_kcontrol_new snd_mbox1_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Clock Source", @@ -728,8 +733,9 @@ static struct snd_kcontrol_new snd_mbox1_switch = { static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer) { - return snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_mbox1_switch, mixer)); + return add_single_ctl_with_resume(mixer, 0, + snd_mbox1_switch_resume, + &snd_mbox1_switch, NULL); } /* Native Instruments device quirks */ -- cgit v0.10.2 From da6d276957ea56b9514aa5c8d885edf22f0b3e65 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 16:59:47 +0100 Subject: ALSA: usb-audio: Add resume support for Native Instruments controls The changes at this time are a bit more wider than previous ones. Firstly, the NI controls didn't cache the values, so I had to implement the caching. It's stored in bit 24 of private_value. In addition to that, the initial values have to be read from registers. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f7ad207..e11b4f3 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -742,64 +742,68 @@ static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer) #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) -static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u8 tmp; - int ret; - - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; - else - ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, wIndex, - &tmp, sizeof(tmp)); - up_read(&mixer->chip->shutdown_rwsem); + unsigned int pval = kctl->private_value; + u8 value; + int err; - if (ret < 0) { + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, pval & 0xffff, &value, 1); + if (err < 0) { dev_err(&dev->dev, - "unable to issue vendor read request (ret = %d)", ret); - return ret; + "unable to issue vendor read request (ret = %d)", err); + return err; } - ucontrol->value.integer.value[0] = tmp; - + kctl->private_value |= (value << 24); return 0; } -static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, +static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u16 wValue = ucontrol->value.integer.value[0]; - int ret; + ucontrol->value.integer.value[0] = kcontrol->private_value >> 24; + return 0; +} - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; +static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list) +{ + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; else - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - wValue, wIndex, - NULL, 0, 1000); - up_read(&mixer->chip->shutdown_rwsem); + err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + pval >> 24, pval & 0xffff, NULL, 0, 1000); + up_read(&chip->shutdown_rwsem); + return err; +} - if (ret < 0) { - dev_err(&dev->dev, - "unable to issue vendor write request (ret = %d)", ret); - return ret; - } +static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + u8 oldval = (kcontrol->private_value >> 24) & 0xff; + u8 newval = ucontrol->value.integer.value[0]; + int err; - return 0; + if (oldval == newval) + return 0; + + kcontrol->private_value &= ~(0xff << 24); + kcontrol->private_value |= newval; + err = snd_ni_update_cur_val(list); + return err < 0 ? err : 1; } static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { @@ -870,16 +874,17 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, }; for (i = 0; i < count; i++) { - struct snd_kcontrol *c; + struct usb_mixer_elem_list *list; template.name = kc[i].name; template.private_value = kc[i].private_value; - c = snd_ctl_new1(&template, mixer); - err = snd_ctl_add(mixer->chip->card, c); - + err = add_single_ctl_with_resume(mixer, 0, + snd_ni_update_cur_val, + &template, &list); if (err < 0) break; + snd_ni_control_init_val(mixer, list->kctl); } return err; -- cgit v0.10.2 From 0b4e9cfcef055a1be9bee5a47262e9cbcf17e8cd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 17:37:40 +0100 Subject: ALSA: usb-audio: Add resume support for FTU controls A few FTU mixer controls have the own value handling, so they have to be rewritten to follow the support for resume callbacks. This ended up in a fair amount of refactoring. Its own struct is now removed, instead the values are embedded in kctl private_value totally. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index e11b4f3..fcb7ae5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -892,14 +892,6 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ /* FTU Effect switch (also used by C400/C600) */ -struct snd_ftu_eff_switch_priv_val { - struct usb_mixer_interface *mixer; - int cached_value; - int is_cached; - int bUnitID; - int validx; -}; - static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -911,138 +903,77 @@ static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } -static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { - struct snd_usb_audio *chip; - struct usb_mixer_interface *mixer; - struct snd_ftu_eff_switch_priv_val *pval; + struct usb_device *dev = mixer->chip->dev; + unsigned int pval = kctl->private_value; int err; unsigned char value[2]; - int id, validx; - - const int val_len = 2; value[0] = 0x00; value[1] = 0x00; - pval = (struct snd_ftu_eff_switch_priv_val *) - kctl->private_value; + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + pval & 0xff00, + snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8), + value, 2); + if (err < 0) + return err; - if (pval->is_cached) { - ucontrol->value.enumerated.item[0] = pval->cached_value; - return 0; - } + kctl->private_value |= value[0] << 24; + return 0; +} - mixer = (struct usb_mixer_interface *) pval->mixer; - if (snd_BUG_ON(!mixer)) - return -EINVAL; +static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value >> 24; + return 0; +} - chip = (struct snd_usb_audio *) mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; +static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list) +{ + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; + unsigned char value[2]; + int err; - id = pval->bUnitID; - validx = pval->validx; + value[0] = pval >> 24; + value[1] = 0; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) err = -ENODEV; else err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - ucontrol->value.enumerated.item[0] = value[0]; - pval->cached_value = value[0]; - pval->is_cached = 1; - - return 0; + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + pval & 0xff00, + snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8), + value, 2); + up_read(&chip->shutdown_rwsem); + return err; } static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct snd_usb_audio *chip; - struct snd_ftu_eff_switch_priv_val *pval; - - struct usb_mixer_interface *mixer; - int changed, cur_val, err, new_val; - unsigned char value[2]; - int id, validx; - - const int val_len = 2; - - changed = 0; + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + unsigned int pval = list->kctl->private_value; + int cur_val, err, new_val; - pval = (struct snd_ftu_eff_switch_priv_val *) - kctl->private_value; - cur_val = pval->cached_value; + cur_val = pval >> 24; new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + return 0; - mixer = (struct usb_mixer_interface *) pval->mixer; - if (snd_BUG_ON(!mixer)) - return -EINVAL; - - chip = (struct snd_usb_audio *) mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; - - id = pval->bUnitID; - validx = pval->validx; - - if (!pval->is_cached) { - /* Read current value */ - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - cur_val = value[0]; - pval->cached_value = cur_val; - pval->is_cached = 1; - } - /* update value if needed */ - if (cur_val != new_val) { - value[0] = new_val; - value[1] = 0; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - pval->cached_value = new_val; - pval->is_cached = 1; - changed = 1; - } - - return changed; -} - -static void kctl_private_value_free(struct snd_kcontrol *kctl) -{ - kfree((void *)kctl->private_value); + kctl->private_value &= ~(0xff << 24); + kctl->private_value |= new_val << 24; + err = snd_ftu_eff_switch_update(list); + return err < 0 ? err : 1; } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, @@ -1057,33 +988,16 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, .get = snd_ftu_eff_switch_get, .put = snd_ftu_eff_switch_put }; - + struct usb_mixer_elem_list *list; int err; - struct snd_kcontrol *kctl; - struct snd_ftu_eff_switch_priv_val *pval; - - pval = kzalloc(sizeof(*pval), GFP_KERNEL); - if (!pval) - return -ENOMEM; - - pval->cached_value = 0; - pval->is_cached = 0; - pval->mixer = mixer; - pval->bUnitID = bUnitID; - pval->validx = validx; - template.private_value = (unsigned long) pval; - kctl = snd_ctl_new1(&template, mixer->chip); - if (!kctl) { - kfree(pval); - return -ENOMEM; - } - - kctl->private_free = kctl_private_value_free; - err = snd_ctl_add(mixer->chip->card, kctl); + err = add_single_ctl_with_resume(mixer, bUnitID, + snd_ftu_eff_switch_update, + &template, &list); if (err < 0) return err; - + list->kctl->private_value = (validx << 8) | bUnitID; + snd_ftu_eff_switch_init(mixer, list->kctl); return 0; } -- cgit v0.10.2 From 288673beae6cbd8198be94284adbaeb5cba7dbda Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Nov 2014 18:06:17 +0100 Subject: ALSA: usb-audio: Add resume support for MicroII SPDIF ctls Like the previous fixes, the mixer accessors are converted to use usb_mixer_elem_list objects. In addition, the proper shutdown check are put in get and put callbacks. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index fcb7ae5..dc9df00 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1509,7 +1509,8 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct snd_usb_audio *chip = list->mixer->chip; int err; struct usb_interface *iface; struct usb_host_interface *alts; @@ -1517,17 +1518,23 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, unsigned char data[3]; int rate; + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; ucontrol->value.iec958.status[2] = 0x00; /* use known values for that card: interface#1 altsetting#1 */ - iface = usb_ifnum_to_if(mixer->chip->dev, 1); + iface = usb_ifnum_to_if(chip->dev, 1); alts = &iface->altsetting[1]; ep = get_endpoint(alts, 0)->bEndpointAddress; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, UAC_EP_CS_ATTR_SAMPLE_RATE << 8, @@ -1542,22 +1549,27 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100; err = 0; -end: + end: + up_read(&chip->shutdown_rwsem); return err; } -static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int err; + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; u8 reg; - unsigned long priv_backup = kcontrol->private_value; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } - reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | - (ucontrol->value.iec958.status[0] & 0x0f); - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1567,15 +1579,10 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end; - kcontrol->private_value &= 0xfffff0f0; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; - kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); - - reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ? - 0xa0 : 0x20; - reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20; + reg |= (pval >> 12) & 0x0f; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1585,16 +1592,36 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end; - kcontrol->private_value &= 0xffff0fff; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; + end: + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + unsigned int pval, pval_old; + int err; + + pval = pval_old = kcontrol->private_value; + pval &= 0xfffff0f0; + pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; + pval |= (ucontrol->value.iec958.status[0] & 0x0f); + + pval &= 0xffff0fff; + pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; /* The frequency bits in AES3 cannot be set via register access. */ /* Silently ignore any bits from the request that cannot be set. */ - err = (priv_backup != kcontrol->private_value); -end: - return err; + if (pval == pval_old) + return 0; + + kcontrol->private_value = pval; + err = snd_microii_spdif_default_update(list); + return err < 0 ? err : 1; } static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, @@ -1616,15 +1643,20 @@ static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, return 0; } -static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct snd_usb_audio *chip = list->mixer->chip; + u8 reg = list->kctl->private_value; int err; - u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1632,15 +1664,27 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, NULL, 0); - if (!err) { - err = (reg != (kcontrol->private_value & 0x0ff)); - if (err) - kcontrol->private_value = reg; - } - + end: + up_read(&chip->shutdown_rwsem); return err; } +static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + u8 reg; + int err; + + reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; + if (reg != list->kctl->private_value) + return 0; + + kcontrol->private_value = reg; + err = snd_microii_spdif_switch_update(list); + return err < 0 ? err : 1; +} + static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1670,10 +1714,17 @@ static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { static int snd_microii_controls_create(struct usb_mixer_interface *mixer) { int err, i; + static usb_mixer_elem_resume_func_t resume_funcs[] = { + snd_microii_spdif_default_update, + NULL, + snd_microii_spdif_switch_update + }; for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); + err = add_single_ctl_with_resume(mixer, 0, + resume_funcs[i], + &snd_microii_mixer_spdif[i], + NULL); if (err < 0) return err; } -- cgit v0.10.2 From b61f90eac1ff9d1b30497e611aba4651d4066706 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Nov 2014 17:20:46 +0100 Subject: ALSA: usb-audio: Add resume support for Scarlett mixers Scarlett driver uses almost compatible usb_mixer_elem_info struct, so we just need to add a couple of simple resume callbacks to handle them accordingly. Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 92dba35..9109652 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -285,6 +285,19 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, return changed; } +static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + int i; + + for (i = 0; i < elem->channels; i++) + if (elem->cached & (1 << i)) + snd_usb_set_cur_mix_value(elem, i, i, + elem->cache_val[i]); + return 0; +} + static int scarlett_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -432,6 +445,16 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, return 0; } +static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + + if (elem->cached) + snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); + return 0; +} + static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -514,6 +537,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_sync = { static int add_new_ctl(struct usb_mixer_interface *mixer, const struct snd_kcontrol_new *ncontrol, + usb_mixer_elem_resume_func_t resume, int index, int offset, int num, int val_type, int channels, const char *name, const struct scarlett_mixer_elem_enum_info *opt, @@ -529,6 +553,7 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, return -ENOMEM; elem->head.mixer = mixer; + elem->head.resume = resume; elem->control = offset; elem->idx_off = num; elem->head.id = index; @@ -548,7 +573,7 @@ static int add_new_ctl(struct usb_mixer_interface *mixer, strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); - err = snd_ctl_add(mixer->chip->card, kctl); + err = snd_usb_mixer_add_control(&elem->head, kctl); if (err < 0) return err; @@ -569,7 +594,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add mute switch */ snprintf(mx, sizeof(mx), "Master %d (%s) Playback Switch", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); if (err < 0) return err; @@ -577,7 +603,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add volume control and initialize to 0 */ snprintf(mx, sizeof(mx), "Master %d (%s) Playback Volume", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); if (err < 0) return err; @@ -585,7 +612,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add L channel source playback enumeration */ snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -594,7 +622,8 @@ static int add_output_ctls(struct usb_mixer_interface *mixer, /* Add R channel source playback enumeration */ snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", index + 1, name); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x33, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -824,13 +853,15 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *elem; /* create master switch and playback volume */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, 0x0a, 0x01, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, 0, USB_MIXER_S16, 1, "Master Playback Switch", NULL, &elem); if (err < 0) return err; - err = add_new_ctl(mixer, &usb_scarlett_ctl_master, 0x0a, 0x02, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, 0, USB_MIXER_S16, 1, "Master Playback Volume", NULL, &elem); if (err < 0) @@ -848,7 +879,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, break; case SCARLETT_SWITCH_IMPEDANCE: sprintf(mx, "Input %d Impedance Switch", ctl->num); - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, 0x09, ctl->num, USB_MIXER_S16, 1, mx, &opt_impedance, &elem); if (err < 0) @@ -856,7 +888,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, break; case SCARLETT_SWITCH_PAD: sprintf(mx, "Input %d Pad Switch", ctl->num); - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x01, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, 0x0b, ctl->num, USB_MIXER_S16, 1, mx, &opt_pad, &elem); if (err < 0) @@ -912,7 +945,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (i = 0; i < info->matrix_in; i++) { snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", i+1); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x32, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x32, 0x06, i, USB_MIXER_S16, 1, mx, &info->opt_matrix, &elem); if (err < 0) @@ -921,7 +955,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (o = 0; o < info->matrix_out; o++) { sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, o+'A'); - err = add_new_ctl(mixer, &usb_scarlett_ctl, 0x3c, 0x00, + err = add_new_ctl(mixer, &usb_scarlett_ctl, + scarlett_ctl_resume, 0x3c, 0x00, (i << 3) + (o & 0x07), USB_MIXER_S16, 1, mx, NULL, &elem); if (err < 0) @@ -933,7 +968,8 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) for (i = 0; i < info->input_len; i++) { snprintf(mx, sizeof(mx), "Input Source %02d Capture Route", i+1); - err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, 0x34, + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x34, 0x00, i, USB_MIXER_S16, 1, mx, &info->opt_master, &elem); if (err < 0) @@ -941,14 +977,15 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) } /* val_len == 1 needed here */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, 0x28, 0x01, 0, + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x28, 0x01, 0, USB_MIXER_U8, 1, "Sample Clock Source", &opt_clock, &elem); if (err < 0) return err; /* val_len == 1 and UAC2_CS_MEM */ - err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, 0x3c, 0x00, 2, + err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2, USB_MIXER_U8, 1, "Sample Clock Sync Status", &opt_sync, &elem); if (err < 0) -- cgit v0.10.2 From 0788148935c3a0fae3cca6af72943b9628bf8a54 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 19 Nov 2014 17:07:43 +0900 Subject: ARM: exynos_defconfig: Enable max77802 rtc and clock drivers Commit 6e80e3d87549 ("ARM: exynos_defconfig: Enable MAX77802") enabled support for the max77802 regulators but the PMIC also has a Real-Time-Clock (RTC) and 2-channel 32kHz clock outputs. Enable the kernel config options to have the drivers for these devices built-in since they are present in many Exynos boards. Signed-off-by: Javier Martinez Canillas Acked-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Kukjin Kim diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index 72058b8..e21ef83 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -142,11 +142,13 @@ CONFIG_MMC_DW_IDMAC=y CONFIG_MMC_DW_EXYNOS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_MAX77802=y CONFIG_RTC_DRV_S5M=y CONFIG_RTC_DRV_S3C=y CONFIG_DMADEVICES=y CONFIG_PL330_DMA=y CONFIG_COMMON_CLK_MAX77686=y +CONFIG_COMMON_CLK_MAX77802=y CONFIG_COMMON_CLK_S2MPS11=y CONFIG_EXYNOS_IOMMU=y CONFIG_IIO=y -- cgit v0.10.2 From ef59a20ba375aeb97b3150a118318884743452a8 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 21 Nov 2014 15:29:00 +0100 Subject: ARM: 8216/1: xscale: correct auxiliary register in suspend/resume According to the manuals I have, XScale auxiliary register should be reached with opc_2 = 1 instead of crn = 1. cpu_xscale_proc_init correctly uses c1, c0, 1 arguments, but cpu_xscale_do_suspend and cpu_xscale_do_resume use c1, c1, 0. Correct suspend/resume functions to also use c1, c0, 1. The issue was primarily noticed thanks to qemu reporing "unsupported instruction" on the pxa suspend path. Confirmed in PXA210/250 and PXA255 XScale Core manuals and in PXA270 and PXA320 Developers Guides. Harware tested by me on tosa (pxa255). Robert confirmed on pxa270 board. Tested-by: Robert Jarzmik Signed-off-by: Dmitry Eremin-Solenikov Acked-by: Robert Jarzmik Cc: stable@vger.kernel.org Signed-off-by: Russell King diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 23259f10..afa2b3c 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -535,7 +535,7 @@ ENTRY(cpu_xscale_do_suspend) mrc p15, 0, r5, c15, c1, 0 @ CP access reg mrc p15, 0, r6, c13, c0, 0 @ PID mrc p15, 0, r7, c3, c0, 0 @ domain ID - mrc p15, 0, r8, c1, c1, 0 @ auxiliary control reg + mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg mrc p15, 0, r9, c1, c0, 0 @ control reg bic r4, r4, #2 @ clear frequency change bit stmia r0, {r4 - r9} @ store cp regs @@ -552,7 +552,7 @@ ENTRY(cpu_xscale_do_resume) mcr p15, 0, r6, c13, c0, 0 @ PID mcr p15, 0, r7, c3, c0, 0 @ domain ID mcr p15, 0, r1, c2, c0, 0 @ translation table base addr - mcr p15, 0, r8, c1, c1, 0 @ auxiliary control reg + mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg mov r0, r9 @ control register b cpu_resume_mmu ENDPROC(cpu_xscale_do_resume) -- cgit v0.10.2 From e60a342bc494228d820dd801e9bfd72fbf829bc4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 19 Nov 2014 15:50:31 +0000 Subject: thermal: sti: Ignore suspend/resume functions when !PM_SLEEP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents build warning: st_thermal.c:278:12: warning: ‘st_thermal_suspend’ defined but not used [-Wunused-function] st_thermal.c:286:12: warning: ‘st_thermal_resume’ defined but not used [-Wunused-function] Acked-by: Maxime Coquelin Signed-off-by: Lee Jones Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index 90163b3..d1ec580 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -275,6 +275,7 @@ int st_thermal_unregister(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(st_thermal_unregister); +#ifdef CONFIG_PM_SLEEP static int st_thermal_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -305,6 +306,8 @@ static int st_thermal_resume(struct device *dev) return 0; } +#endif + SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); EXPORT_SYMBOL_GPL(st_thermal_pm_ops); -- cgit v0.10.2 From d712eaf29d3fe5928d891a4a90ac58644ad595ed Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 18:34:48 +0100 Subject: ALSA: core: Deletion of unnecessary checks before two function calls The functions snd_seq_oss_timer_delete() and vunmap() perform also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index b9184d2..b0e32e1 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -403,14 +403,11 @@ free_devinfo(void *private) { struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private; - if (dp->timer) - snd_seq_oss_timer_delete(dp->timer); + snd_seq_oss_timer_delete(dp->timer); - if (dp->writeq) - snd_seq_oss_writeq_delete(dp->writeq); + snd_seq_oss_writeq_delete(dp->writeq); - if (dp->readq) - snd_seq_oss_readq_delete(dp->readq); + snd_seq_oss_readq_delete(dp->readq); kfree(dp); } diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index 0a41850..84fffab 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -39,8 +39,7 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) if (! sgbuf) return -EINVAL; - if (dmab->area) - vunmap(dmab->area); + vunmap(dmab->area); dmab->area = NULL; tmpb.dev.type = SNDRV_DMA_TYPE_DEV; -- cgit v0.10.2 From 42d772101274492ca15258ea346e6ebbc2bc9bd0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 19:05:50 +0100 Subject: ALSA: es1688_lib: Deletion of an unnecessary check before the function call "release_and_free_resource" The release_and_free_resource() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index de810e4..b545014 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -614,8 +614,7 @@ static int snd_es1688_free(struct snd_es1688 *chip) { if (chip->hardware != ES1688_HW_UNDEF) snd_es1688_init(chip, 0); - if (chip->res_port) - release_and_free_resource(chip->res_port); + release_and_free_resource(chip->res_port); if (chip->irq >= 0) free_irq(chip->irq, (void *) chip); if (chip->dma8 >= 0) { -- cgit v0.10.2 From 966b7bc9354ab8f59f0ef2d96306615157e0f76e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 19:32:02 +0100 Subject: ALSA: sb: Deletion of unnecessary checks before two function calls The functions release_and_free_resource() and snd_util_memhdr_free() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 4e3fcfb..95b39be 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -105,8 +105,7 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) snd_device_free(dev->card, hw->pcm); if (hw->emu) snd_emux_free(hw->emu); - if (hw->memhdr) - snd_util_memhdr_free(hw->memhdr); + snd_util_memhdr_free(hw->memhdr); hw->emu = NULL; hw->memhdr = NULL; return 0; diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index 3ef9906..f22b448 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -184,8 +184,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip) static int snd_sbdsp_free(struct snd_sb *chip) { - if (chip->res_port) - release_and_free_resource(chip->res_port); + release_and_free_resource(chip->res_port); if (chip->irq >= 0) free_irq(chip->irq, (void *) chip); #ifdef CONFIG_ISA -- cgit v0.10.2 From d3e19567fa85e1a0dab02205b89b2908084ecadd Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 21 Nov 2014 17:11:49 +0100 Subject: thermal: Exynos: Deletion of unnecessary checks before two function calls The functions cpufreq_cooling_unregister() and thermal_zone_device_unregister() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index 3f5ad25..b6be572 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -417,13 +417,10 @@ void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) th_zone = sensor_conf->pzone_data; - if (th_zone->therm_dev) - thermal_zone_device_unregister(th_zone->therm_dev); + thermal_zone_device_unregister(th_zone->therm_dev); - for (i = 0; i < th_zone->cool_dev_size; i++) { - if (th_zone->cool_dev[i]) - cpufreq_cooling_unregister(th_zone->cool_dev[i]); - } + for (i = 0; i < th_zone->cool_dev_size; ++i) + cpufreq_cooling_unregister(th_zone->cool_dev[i]); dev_info(sensor_conf->dev, "Exynos: Kernel Thermal management unregistered\n"); -- cgit v0.10.2 From e7820e39b7d19b9fe1928fc19de9361b44150ca6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 21 Nov 2014 11:47:16 -0800 Subject: net: Revert "net: avoid one atomic operation in skb_clone()" Not sure what I was thinking, but doing anything after releasing a refcount is suicidal or/and embarrassing. By the time we set skb->fclone to SKB_FCLONE_FREE, another cpu could have released last reference and freed whole skb. We potentially corrupt memory or trap if CONFIG_DEBUG_PAGEALLOC is set. Reported-by: Chris Mason Fixes: ce1a4ea3f1258 ("net: avoid one atomic operation in skb_clone()") Signed-off-by: Eric Dumazet Cc: Sabrina Dubroca Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c16615b..32e31c2 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -552,20 +552,13 @@ static void kfree_skbmem(struct sk_buff *skb) case SKB_FCLONE_CLONE: fclones = container_of(skb, struct sk_buff_fclones, skb2); - /* Warning : We must perform the atomic_dec_and_test() before - * setting skb->fclone back to SKB_FCLONE_FREE, otherwise - * skb_clone() could set clone_ref to 2 before our decrement. - * Anyway, if we are going to free the structure, no need to - * rewrite skb->fclone. + /* The clone portion is available for + * fast-cloning again. */ - if (atomic_dec_and_test(&fclones->fclone_ref)) { + skb->fclone = SKB_FCLONE_FREE; + + if (atomic_dec_and_test(&fclones->fclone_ref)) kmem_cache_free(skbuff_fclone_cache, fclones); - } else { - /* The clone portion is available for - * fast-cloning again. - */ - skb->fclone = SKB_FCLONE_FREE; - } break; } } @@ -887,11 +880,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) if (skb->fclone == SKB_FCLONE_ORIG && n->fclone == SKB_FCLONE_FREE) { n->fclone = SKB_FCLONE_CLONE; - /* As our fastclone was free, clone_ref must be 1 at this point. - * We could use atomic_inc() here, but it is faster - * to set the final value. - */ - atomic_set(&fclones->fclone_ref, 2); + atomic_inc(&fclones->fclone_ref); } else { if (skb_pfmemalloc(skb)) gfp_mask |= __GFP_MEMALLOC; -- cgit v0.10.2 From 0c228e833c88e3aa029250f5db77d5968c5ce5b5 Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Thu, 20 Nov 2014 15:09:53 -0800 Subject: tcp: Restore RFC5961-compliant behavior for SYN packets Commit c3ae62af8e755 ("tcp: should drop incoming frames without ACK flag set") was created to mitigate a security vulnerability in which a local attacker is able to inject data into locally-opened sockets by using TCP protocol statistics in procfs to quickly find the correct sequence number. This broke the RFC5961 requirement to send a challenge ACK in response to spurious RST packets, which was subsequently fixed by commit 7b514a886ba50 ("tcp: accept RST without ACK flag"). Unfortunately, the RFC5961 requirement that spurious SYN packets be handled in a similar manner remains broken. RFC5961 section 4 states that: ... the handling of the SYN in the synchronized state SHOULD be performed as follows: 1) If the SYN bit is set, irrespective of the sequence number, TCP MUST send an ACK (also referred to as challenge ACK) to the remote peer: After sending the acknowledgment, TCP MUST drop the unacceptable segment and stop processing further. By sending an ACK, the remote peer is challenged to confirm the loss of the previous connection and the request to start a new connection. A legitimate peer, after restart, would not have a TCB in the synchronized state. Thus, when the ACK arrives, the peer should send a RST segment back with the sequence number derived from the ACK field that caused the RST. This RST will confirm that the remote peer has indeed closed the previous connection. Upon receipt of a valid RST, the local TCP endpoint MUST terminate its connection. The local TCP endpoint should then rely on SYN retransmission from the remote end to re-establish the connection. This patch lets SYN packets through the discard added in c3ae62af8e755, so that spurious SYN packets are properly dealt with as per the RFC. The challenge ACK is sent unconditionally and is rate-limited, so the original vulnerability is not reintroduced by this patch. Signed-off-by: Calvin Owens Acked-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88fa2d1..d107ee2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5231,7 +5231,7 @@ slow_path: if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb)) goto csum_error; - if (!th->ack && !th->rst) + if (!th->ack && !th->rst && !th->syn) goto discard; /* @@ -5650,7 +5650,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, goto discard; } - if (!th->ack && !th->rst) + if (!th->ack && !th->rst && !th->syn) goto discard; if (!tcp_validate_incoming(sk, skb, th, 0)) -- cgit v0.10.2 From 8daee1352d51a32676b84bddcc0e3252d1caa833 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Nov 2014 13:28:03 +0100 Subject: USB: uas: Add no-uas quirk for Hitachi usb-3 enclosures 4971:1012 These disks have a broken uas implementation, the tag field of the status iu-s is not set properly, so we need to fall-back to usb-storage for these. Cc: stable@vger.kernel.org # 3.16 Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 2fefaf9..18a283d 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -103,3 +103,10 @@ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, "VL711", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), + +/* Reported-by: Hans de Goede */ +UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, + "Hitachi", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), -- cgit v0.10.2 From c3492dbfa1050debf23a5b5cd2bc7514c5b37896 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 18 Nov 2014 11:27:11 +0200 Subject: USB: xhci: don't start a halted endpoint before its new dequeue is set A halted endpoint ring must first be reset, then move the ring dequeue pointer past the problematic TRB. If we start the ring too early after reset, but before moving the dequeue pointer we will end up executing the same problematic TRB again. As we always issue a set transfer dequeue command after a reset endpoint command we can skip starting endpoint rings at reset endpoint command completion. Without this fix we end up trying to handle the same faulty TD for contol endpoints. causing timeout, and failing testusb ctrl_out write tests. Fixes: e9df17e (USB: xhci: Correct assumptions about number of rings per endpoint.) Cc: #v2.6.35 Tested-by: Felipe Balbi Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bc6fcbc..fbd0342 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1067,9 +1067,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, false); xhci_ring_cmd_db(xhci); } else { - /* Clear our internal halted state and restart the ring(s) */ + /* Clear our internal halted state */ xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } } -- cgit v0.10.2 From 9b41ebd3cf0f68d8cad779d3eeba336f78262e43 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Tue, 18 Nov 2014 11:27:13 +0200 Subject: Revert "xhci: clear root port wake on bits if controller isn't wake-up capable" commit ff8cbf250b44 ("xhci: clear root port wake on bits if controller isn't") can cause device detection error if runtime PM is enabled, and S3 wake is disabled. Revert it. https://bugzilla.kernel.org/show_bug.cgi?id=85701 This commit got into stable and should be reverted from there as well. Cc: stable # v3.2+ Signed-off-by: Lu Baolu Reported-by: Dmitry Nezhevenko [Mathias Nyman: reword commit message] Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 696160d..388cfd8 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -22,7 +22,6 @@ #include -#include #include #include "xhci.h" @@ -1149,9 +1148,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) * including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME * is enabled, so also enable remote wake here. */ - if (hcd->self.root_hub->do_remote_wakeup - && device_may_wakeup(hcd->self.controller)) { - + if (hcd->self.root_hub->do_remote_wakeup) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; t2 &= ~PORT_WKCONN_E; -- cgit v0.10.2 From 8e71a322fdb127814bcba423a512914ca5bc6cf5 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 18 Nov 2014 11:27:12 +0200 Subject: USB: xhci: Reset a halted endpoint immediately when we encounter a stall. If a device is halted and reuturns a STALL, then the halted endpoint needs to be cleared both on the host and device side. The host side halt is cleared by issueing a xhci reset endpoint command. The device side is cleared with a ClearFeature(ENDPOINT_HALT) request, which should be issued by the device driver if a URB reruen -EPIPE. Previously we cleared the host side halt after the device side was cleared. To make sure the host side halt is cleared in time we want to issue the reset endpoint command immedialtely when a STALL status is encountered. Otherwise we end up not following the specs and not returning -EPIPE several times in a row when trying to transfer data to a halted endpoint. Fixes: bcef3fd (USB: xhci: Handle errors that cause endpoint halts.) Cc: # v2.6.33+ Tested-by: Felipe Balbi Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index fbd0342..06433ae 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1822,22 +1822,13 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, ep->stopped_td = td; return 0; } else { - if (trb_comp_code == COMP_STALL) { - /* The transfer is completed from the driver's - * perspective, but we need to issue a set dequeue - * command for this stalled endpoint to move the dequeue - * pointer past the TD. We can't do that here because - * the halt condition must be cleared first. Let the - * USB class driver clear the stall later. - */ - ep->stopped_td = td; - ep->stopped_stream = ep_ring->stream_id; - } else if (xhci_requires_manual_halt_cleanup(xhci, - ep_ctx, trb_comp_code)) { - /* Other types of errors halt the endpoint, but the - * class driver doesn't call usb_reset_endpoint() unless - * the error is -EPIPE. Clear the halted status in the - * xHCI hardware manually. + if (trb_comp_code == COMP_STALL || + xhci_requires_manual_halt_cleanup(xhci, ep_ctx, + trb_comp_code)) { + /* Issue a reset endpoint command to clear the host side + * halt, followed by a set dequeue command to move the + * dequeue pointer past the TD. + * The class driver clears the device side halt later. */ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, ep_ring->stream_id, @@ -1957,9 +1948,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, else td->urb->actual_length = 0; - xhci_cleanup_halted_endpoint(xhci, - slot_id, ep_index, 0, td, event_trb); - return finish_td(xhci, td, event_trb, event, ep, status, true); + return finish_td(xhci, td, event_trb, event, ep, status, false); } /* * Did we transfer any data, despite the errors that might have @@ -2518,17 +2507,8 @@ cleanup: if (ret) { urb = td->urb; urb_priv = urb->hcpriv; - /* Leave the TD around for the reset endpoint function - * to use(but only if it's not a control endpoint, - * since we already queued the Set TR dequeue pointer - * command for stalled control endpoints). - */ - if (usb_endpoint_xfer_control(&urb->ep->desc) || - (trb_comp_code != COMP_STALL && - trb_comp_code != COMP_BABBLE)) - xhci_urb_free_priv(xhci, urb_priv); - else - kfree(urb_priv); + + xhci_urb_free_priv(xhci, urb_priv); usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); if ((urb->actual_length != urb->transfer_buffer_length && diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2a5d45b..4d0d240 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2912,68 +2912,33 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, } } -/* Deal with stalled endpoints. The core should have sent the control message - * to clear the halt condition. However, we need to make the xHCI hardware - * reset its sequence number, since a device will expect a sequence number of - * zero after the halt condition is cleared. +/* Called when clearing halted device. The core should have sent the control + * message to clear the device halt condition. The host side of the halt should + * already be cleared with a reset endpoint command issued when the STALL tx + * event was received. + * * Context: in_interrupt */ + void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { struct xhci_hcd *xhci; - struct usb_device *udev; - unsigned int ep_index; - unsigned long flags; - int ret; - struct xhci_virt_ep *virt_ep; - struct xhci_command *command; xhci = hcd_to_xhci(hcd); - udev = (struct usb_device *) ep->hcpriv; - /* Called with a root hub endpoint (or an endpoint that wasn't added - * with xhci_add_endpoint() - */ - if (!ep->hcpriv) - return; - ep_index = xhci_get_endpoint_index(&ep->desc); - virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index]; - if (!virt_ep->stopped_td) { - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Endpoint 0x%x not halted, refusing to reset.", - ep->desc.bEndpointAddress); - return; - } - if (usb_endpoint_xfer_control(&ep->desc)) { - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Control endpoint stall already handled."); - return; - } - - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); - if (!command) - return; - xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, - "Queueing reset endpoint command"); - spin_lock_irqsave(&xhci->lock, flags); - ret = xhci_queue_reset_ep(xhci, command, udev->slot_id, ep_index); /* - * Can't change the ring dequeue pointer until it's transitioned to the - * stopped state, which is only upon a successful reset endpoint - * command. Better hope that last command worked! + * We might need to implement the config ep cmd in xhci 4.8.1 note: + * The Reset Endpoint Command may only be issued to endpoints in the + * Halted state. If software wishes reset the Data Toggle or Sequence + * Number of an endpoint that isn't in the Halted state, then software + * may issue a Configure Endpoint Command with the Drop and Add bits set + * for the target endpoint. that is in the Stopped state. */ - if (!ret) { - xhci_cleanup_stalled_ring(xhci, udev, ep_index); - kfree(virt_ep->stopped_td); - xhci_ring_cmd_db(xhci); - } - virt_ep->stopped_td = NULL; - virt_ep->stopped_stream = 0; - spin_unlock_irqrestore(&xhci->lock, flags); - if (ret) - xhci_warn(xhci, "FIXME allocate a new ring segment\n"); + /* For now just print debug to follow the situation */ + xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n", + ep->desc.bEndpointAddress); } static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, -- cgit v0.10.2 From a1377e5397ab321e21b793ec8cd2b6f12bd3c718 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Tue, 18 Nov 2014 11:27:14 +0200 Subject: usb: xhci: rework root port wake bits if controller isn't allowed to wakeup When system is being suspended, if host device is not allowed to do wakeup, xhci_suspend() needs to clear all root port wake on bits. Otherwise, some platforms may generate spurious wakeup, even if PCI PME# is disabled. The initial commit ff8cbf250b44 ("xhci: clear root port wake on bits"), which also got into stable, turned out to not work correctly and had to be reverted, and is now rewritten. Cc: stable # v3.2+ Signed-off-by: Lu Baolu Suggested-by: Alan Stern Acked-by: Alan Stern [Mathias Nyman: reword commit message] Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 9a69b1f..142b601 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -281,7 +281,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) pdev->no_d3cold = true; - return xhci_suspend(xhci); + return xhci_suspend(xhci, do_wakeup); } static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 3d78b0c..646300c 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -204,7 +204,15 @@ static int xhci_plat_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - return xhci_suspend(xhci); + /* + * xhci_suspend() needs `do_wakeup` to know whether host is allowed + * to do wakeup during suspend. Since xhci_plat_suspend is currently + * only designed for system suspend, device_may_wakeup() is enough + * to dertermine whether host is allowed to do wakeup. Need to + * reconsider this when xhci_plat_suspend enlarges its scope, e.g., + * also applies to runtime suspend. + */ + return xhci_suspend(xhci, device_may_wakeup(dev)); } static int xhci_plat_resume(struct device *dev) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4d0d240..033b46c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -35,6 +35,8 @@ #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" +#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E) + /* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */ static int link_quirk; module_param(link_quirk, int, S_IRUGO | S_IWUSR); @@ -851,13 +853,47 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) xhci_set_cmd_ring_deq(xhci); } +static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) +{ + int port_index; + __le32 __iomem **port_array; + unsigned long flags; + u32 t1, t2; + + spin_lock_irqsave(&xhci->lock, flags); + + /* disble usb3 ports Wake bits*/ + port_index = xhci->num_usb3_ports; + port_array = xhci->usb3_ports; + while (port_index--) { + t1 = readl(port_array[port_index]); + t1 = xhci_port_state_to_neutral(t1); + t2 = t1 & ~PORT_WAKE_BITS; + if (t1 != t2) + writel(t2, port_array[port_index]); + } + + /* disble usb2 ports Wake bits*/ + port_index = xhci->num_usb2_ports; + port_array = xhci->usb2_ports; + while (port_index--) { + t1 = readl(port_array[port_index]); + t1 = xhci_port_state_to_neutral(t1); + t2 = t1 & ~PORT_WAKE_BITS; + if (t1 != t2) + writel(t2, port_array[port_index]); + } + + spin_unlock_irqrestore(&xhci->lock, flags); +} + /* * Stop HC (not bus-specific) * * This is called when the machine transition into S3/S4 mode. * */ -int xhci_suspend(struct xhci_hcd *xhci) +int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) { int rc = 0; unsigned int delay = XHCI_MAX_HALT_USEC; @@ -868,6 +904,10 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + /* Clear root port wake on bits if wakeup not allowed. */ + if (!do_wakeup) + xhci_disable_port_wake_on_bits(xhci); + /* Don't poll the roothubs on bus suspend. */ xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index df76d64..d745715 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1746,7 +1746,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)); #ifdef CONFIG_PM -int xhci_suspend(struct xhci_hcd *xhci); +int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); int xhci_resume(struct xhci_hcd *xhci, bool hibernated); #else #define xhci_suspend NULL -- cgit v0.10.2 From 4fc390a198c41a007c02f82f41ad100c7c67b55e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 22 Nov 2014 21:29:01 +0300 Subject: sound: oss: uart401: remove unneeded NULL check "devc" can't be NULL here so there is no need to check. Also I removed the "devc = NULL" assignment because devc is stored on stack so it's a no-op. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c index 279bc56..dae4d43 100644 --- a/sound/oss/uart401.c +++ b/sound/oss/uart401.c @@ -412,13 +412,10 @@ void unload_uart401(struct address_info *hw_config) if (!devc->share_irq) free_irq(devc->irq, devc); - if (devc) - { - kfree(midi_devs[devc->my_dev]->converter); - kfree(midi_devs[devc->my_dev]); - kfree(devc); - devc = NULL; - } + kfree(midi_devs[devc->my_dev]->converter); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + /* This kills midi_devs[x] */ sound_unload_mididev(hw_config->slots[4]); } -- cgit v0.10.2 From e9886ab06c1ef42451307c9367e344b2d8140e0b Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:48 +1300 Subject: ALSA: asihpi: Minor string and dead code cleanup Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index ff9f9f1c..0e130dd 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -28,7 +28,6 @@ #include "hpioctl.h" #include "hpicmn.h" - #include #include #include @@ -47,7 +46,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. "); -MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx " +MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx " HPI_VER_STRING); #if defined CONFIG_SND_DEBUG_VERBOSE @@ -87,11 +86,11 @@ MODULE_PARM_DESC(enable_hpi_hwdep, #ifdef KERNEL_ALSA_BUILD static char *build_info = "Built using headers from kernel source"; module_param(build_info, charp, S_IRUGO); -MODULE_PARM_DESC(build_info, "built using headers from kernel source"); +MODULE_PARM_DESC(build_info, "Built using headers from kernel source"); #else static char *build_info = "Built within ALSA source"; module_param(build_info, charp, S_IRUGO); -MODULE_PARM_DESC(build_info, "built within ALSA source"); +MODULE_PARM_DESC(build_info, "Built within ALSA source"); #endif /* set to 1 to dump every control from adapter to log */ @@ -538,7 +537,7 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * int expiry; expiry = HZ / 200; - /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */ + expiry = max(expiry, 1); /* don't let it be zero! */ dpcm->timer.expires = jiffies + expiry; dpcm->respawn_timer = 1; @@ -2932,10 +2931,6 @@ static struct pci_driver driver = { .id_table = asihpi_pci_tbl, .probe = snd_asihpi_probe, .remove = snd_asihpi_remove, -#ifdef CONFIG_PM_SLEEP -/* .suspend = snd_asihpi_suspend, - .resume = snd_asihpi_resume, */ -#endif }; static int __init snd_asihpi_init(void) -- cgit v0.10.2 From 3872f19d96a55ec1d1e7af904d84457d91ef5a63 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:49 +1300 Subject: ALSA: asihpi: New I/O types - AVB & BLUlink, DAB Rf receiver Audio cards wth have AVB or BLU Link IO. Tuner card with DAB receiver Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 0e130dd..628ef7f 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -109,7 +109,7 @@ static int adapter_fs = DEFAULT_SAMPLERATE; struct clk_source { int source; int index; - char *name; + const char *name; }; struct clk_cache { @@ -1292,8 +1292,9 @@ static const char * const asihpi_tuner_band_names[] = { "TV PAL I", "TV PAL DK", "TV SECAM", + "TV DAB", }; - +/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_tuner_band_names) == (HPI_TUNER_BAND_LAST+1)), @@ -1313,9 +1314,11 @@ static const char * const asihpi_src_names[] = { "Analog", "Adapter", "RTP", - "Internal" + "Internal", + "AVB", + "BLU-Link" }; - +/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_src_names) == (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), @@ -1331,8 +1334,11 @@ static const char * const asihpi_dst_names[] = { "Net", "Analog", "RTP", + "AVB", + "Internal", + "BLU-Link" }; - +/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_dst_names) == (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)), @@ -2288,13 +2294,18 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, /*------------------------------------------------------------ Sampleclock source controls ------------------------------------------------------------*/ -static char *sampleclock_sources[MAX_CLOCKSOURCES] = { +static const char const *sampleclock_sources[] = { "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", "SMPTE", "Digital1", "Auto", "Network", "Invalid", - "Prev Module", + "Prev Module", "BLU-Link", "Digital2", "Digital3", "Digital4", "Digital5", "Digital6", "Digital7", "Digital8"}; + /* Number of strings must match expected enumerated values */ + compile_time_assert( + (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES), + assert_sampleclock_sources_size); + static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h index 2088724..4466bd2 100644 --- a/sound/pci/asihpi/hpi.h +++ b/sound/pci/asihpi/hpi.h @@ -196,8 +196,10 @@ enum HPI_SOURCENODES { packets of RTP audio samples from other devices. */ HPI_SOURCENODE_RTP_DESTINATION = 112, HPI_SOURCENODE_INTERNAL = 113, /**< node internal to the device. */ + HPI_SOURCENODE_AVB = 114, /**< AVB input stream */ + HPI_SOURCENODE_BLULINK = 115, /**< BLU-link input channel */ /* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */ - HPI_SOURCENODE_LAST_INDEX = 113 /**< largest ID */ + HPI_SOURCENODE_LAST_INDEX = 115 /**< largest ID */ /* AX6 max sourcenode types = 15 */ }; @@ -224,8 +226,11 @@ enum HPI_DESTNODES { /** RTP stream output node - This node is a source for packets of RTP audio samples that are sent to other devices. */ HPI_DESTNODE_RTP_SOURCE = 208, + HPI_DESTNODE_AVB = 209, /**< AVB output stream */ + HPI_DESTNODE_INTERNAL = 210, /**< node internal to the device. */ + HPI_DESTNODE_BLULINK = 211, /**< BLU-link output channel. */ /* !!!Update this AND hpidebug.h if you add a new destnode type!!! */ - HPI_DESTNODE_LAST_INDEX = 208 /**< largest ID */ + HPI_DESTNODE_LAST_INDEX = 211 /**< largest ID */ /* AX6 max destnode types = 15 */ }; @@ -752,7 +757,8 @@ enum HPI_TUNER_BAND { HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/ HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/ HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/ - HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */ + HPI_TUNER_BAND_DAB = 10, + HPI_TUNER_BAND_LAST = 10 /**< the index of the last tuner band. */ }; /** Tuner mode attributes @@ -842,8 +848,10 @@ enum HPI_SAMPLECLOCK_SOURCES { HPI_SAMPLECLOCK_SOURCE_NETWORK = 8, /** From previous adjacent module (ASI2416 only)*/ HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10, +/** Blu link sample clock*/ + HPI_SAMPLECLOCK_SOURCE_BLULINK = 11, /*! Update this if you add a new clock source.*/ - HPI_SAMPLECLOCK_SOURCE_LAST = 10 + HPI_SAMPLECLOCK_SOURCE_LAST = 11 }; /** Equalizer filter types. Used by HPI_ParametricEq_SetBand() -- cgit v0.10.2 From 35a8dc1f66a0fa88144fcbcd562eb2b2c1e36f11 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:50 +1300 Subject: ALSA: asihpi: Logging format improvements Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 628ef7f..c069033 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -288,21 +288,17 @@ static void print_hwparams(struct snd_pcm_substream *substream, { char name[16]; snd_pcm_debug_name(substream, name, sizeof(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_printdd("%s HWPARAMS\n", name); + snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n", + params_rate(p), params_channels(p), + params_format(p), params_subformat(p)); + snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n", + params_buffer_bytes(p), params_period_bytes(p), + params_period_size(p), params_periods(p)); + snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n", + params_buffer_size(p), params_access(p), + params_rate(p) * params_channels(p) * snd_pcm_format_width(params_format(p)) / 8); - } static snd_pcm_format_t hpi_to_alsa_formats[] = { @@ -480,7 +476,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { snd_printdd( - "stream_host_buffer_attach succeeded %u %lu\n", + "stream_host_buffer_attach success %u %lu\n", params_buffer_bytes(params), (unsigned long)runtime->dma_addr); } else { @@ -490,12 +486,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, } err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, - &dpcm->hpi_buffer_attached, - NULL, NULL, NULL); - - snd_printdd("stream_host_buffer_attach status 0x%x\n", - dpcm->hpi_buffer_attached); - + &dpcm->hpi_buffer_attached, NULL, NULL, NULL); } bytes_per_sec = params_rate(params) * params_channels(params); width = snd_pcm_format_width(params_format(params)); @@ -563,10 +554,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printdd("%s trigger\n", name); switch (cmd) { case SNDRV_PCM_TRIGGER_START: + snd_printdd("%s trigger start\n", name); snd_pcm_group_for_each_entry(s, substream) { struct snd_pcm_runtime *runtime = s->runtime; struct snd_card_asihpi_pcm *ds = runtime->private_data; @@ -587,7 +578,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, * data?? */ unsigned int preload = ds->period_bytes * 1; - snd_printddd("%d preload x%x\n", s->number, preload); + snd_printddd("%d preload %d\n", s->number, preload); hpi_handle_error(hpi_outstream_write_buf( ds->h_stream, &runtime->dma_area[0], @@ -610,7 +601,6 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - snd_printdd("start\n"); /* start the master stream */ snd_card_asihpi_pcm_timer_start(substream); if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || @@ -619,6 +609,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_STOP: + snd_printdd("%s trigger stop\n", name); snd_card_asihpi_pcm_timer_stop(substream); snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) @@ -637,7 +628,6 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - snd_printdd("stop\n"); /* _prepare and _hwparams reset the stream */ hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); @@ -650,12 +640,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printdd("pause release\n"); + snd_printdd("%s trigger pause release\n", name); hpi_handle_error(hpi_stream_start(dpcm->h_stream)); snd_card_asihpi_pcm_timer_start(substream); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printdd("pause\n"); + snd_printdd("%s trigger pause push\n", name); snd_card_asihpi_pcm_timer_stop(substream); hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; @@ -730,7 +720,6 @@ static void snd_card_asihpi_timer_function(unsigned long data) snd_pcm_debug_name(substream, name, sizeof(name)); - 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) { @@ -790,19 +779,20 @@ static void snd_card_asihpi_timer_function(unsigned long data) newdata); } - snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n", + snd_printddd("timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", + name, s->number, state, + ds->pcm_buf_elapsed_dma_ofs, + ds->pcm_buf_host_rw_ofs, + pcm_buf_dma_ofs, + (int)bytes_avail, + + (int)on_card_bytes, + buffer_size-bytes_avail, (unsigned long)frames_to_bytes(runtime, runtime->status->hw_ptr), (unsigned long)frames_to_bytes(runtime, - runtime->control->appl_ptr)); - - 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); + runtime->control->appl_ptr) + ); loops++; } pcm_buf_dma_ofs = min_buf_pos; @@ -820,7 +810,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 0x%04X newdata 0x%04X xfer 0x%04X\n", + snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); snd_pcm_group_for_each_entry(s, substream) { @@ -852,7 +842,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) } if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - snd_printddd("P%d write1 0x%04X 0x%04X\n", + snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfer1, buf_ofs); hpi_handle_error( hpi_outstream_write_buf( @@ -862,7 +852,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("P%d write2 0x%04X 0x%04X\n", + snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfercount - xfer1, buf_ofs); hpi_handle_error( @@ -872,7 +862,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) &ds->format)); } } else { - snd_printddd("C%d read1 0x%04x\n", + snd_printddd("read1, C=%d, xfer=%d\n", s->number, xfer1); hpi_handle_error( hpi_instream_read_buf( @@ -880,7 +870,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) pd, xfer1)); if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("C%d read2 0x%04x\n", + snd_printddd("read2, C=%d, xfer=%d\n", s->number, xfer2); hpi_handle_error( hpi_instream_read_buf( @@ -933,7 +923,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_debug_name(substream, name, sizeof(name)); ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); - snd_printddd("%s pointer = 0x%04lx\n", name, (unsigned long)ptr); + snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr); return ptr; } @@ -1081,9 +1071,10 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printddd("capture pointer %d=%d\n", - substream->number, dpcm->pcm_buf_dma_ofs); + snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); /* NOTE Unlike playback can't use actual samples_played for the capture position, because those samples aren't yet in the local buffer available for reading. @@ -2867,6 +2858,8 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->in_min_chans = 1; } + snd_printk(KERN_INFO "update_interval_frames: %d", + asihpi->update_interval_frames); snd_printk(KERN_INFO "Has dma:%d, grouping:%d, mrx:%d\n", asihpi->can_dma, asihpi->support_grouping, -- cgit v0.10.2 From e51c58c982a0f81baa6fdee7331a8700c8586be5 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:51 +1300 Subject: ALSA: asihpi: Use CONFIG_64BIT directly Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index bc86cb7..c9bdc28 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -554,17 +554,21 @@ struct hpi_pci { struct pci_dev *pci_dev; }; +/** Adapter specification resource */ +struct hpi_adapter_specification { + u32 type; + u8 modules[4]; +}; + struct hpi_resource { union { const struct hpi_pci *pci; const char *net_if; + struct hpi_adapter_specification adapter_spec; + const void *sw_if; } r; -#ifndef HPI64BIT /* keep structure size constant */ - u32 pad_to64; -#endif u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */ u16 padding; - }; /** Format info used inside struct hpi_message @@ -582,7 +586,7 @@ struct hpi_msg_format { struct hpi_msg_data { struct hpi_msg_format format; u8 *pb_data; -#ifndef HPI64BIT +#ifndef CONFIG_64BIT u32 padding; #endif u32 data_size; @@ -595,7 +599,7 @@ struct hpi_data_legacy32 { u32 data_size; }; -#ifdef HPI64BIT +#ifdef CONFIG_64BIT /* Compatibility version of struct hpi_data*/ struct hpi_data_compat32 { struct hpi_msg_format format; diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index d3fbd0d..d17d017 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -41,10 +41,6 @@ HPI Operating System Specific macros for Linux Kernel driver #define HPI_NO_OS_FILE_OPS -#ifdef CONFIG_64BIT -#define HPI64BIT -#endif - /** Details of a memory area allocated with pci_alloc_consistent Need all info for parameters to pci_free_consistent */ -- cgit v0.10.2 From c1464a885444dd7e9c4491177ee102b64adc46c5 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:52 +1300 Subject: ALSA: asihpi: Refactor control cache code. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index 7ed5c26..c775124 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -206,6 +206,14 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) struct hpi_control_cache_info *info = (struct hpi_control_cache_info *) &p_master_cache[byte_count]; + u16 control_index = info->control_index; + + if (control_index >= pC->control_count) { + HPI_DEBUG_LOG(INFO, + "adap %d control index %d out of range, cache not ready?\n", + pC->adap_idx, control_index); + return 0; + } if (!info->size_in32bit_words) { if (!i) { @@ -225,10 +233,10 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) } if (info->control_type) { - pC->p_info[info->control_index] = info; + pC->p_info[control_index] = info; cached++; } else { /* dummy cache entry */ - pC->p_info[info->control_index] = NULL; + pC->p_info[control_index] = NULL; } byte_count += info->size_in32bit_words * 4; @@ -309,35 +317,18 @@ static const struct pad_ofs_size pad_desc[] = { /** CheckControlCache checks the cache and fills the struct hpi_response * accordingly. It returns one if a cache hit occurred, zero otherwise. */ -short hpi_check_control_cache(struct hpi_control_cache *p_cache, +short hpi_check_control_cache_single(struct hpi_control_cache_single *pC, struct hpi_message *phm, struct hpi_response *phr) { - short found = 1; - struct hpi_control_cache_info *pI; - struct hpi_control_cache_single *pC; size_t response_size; - if (!find_control(phm->obj_index, p_cache, &pI)) { - HPI_DEBUG_LOG(VERBOSE, - "HPICMN find_control() failed for adap %d\n", - phm->adapter_index); - return 0; - } - - phr->error = 0; - phr->specific_error = 0; - phr->version = 0; + short found = 1; /* set the default response size */ response_size = sizeof(struct hpi_response_header) + sizeof(struct hpi_control_res); - /* pC is the default cached control strucure. May be cast to - something else in the following switch statement. - */ - pC = (struct hpi_control_cache_single *)pI; - - switch (pI->control_type) { + switch (pC->u.i.control_type) { case HPI_CONTROL_METER: if (phm->u.c.attribute == HPI_METER_PEAK) { @@ -467,7 +458,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, break; case HPI_CONTROL_PAD:{ struct hpi_control_cache_pad *p_pad; - p_pad = (struct hpi_control_cache_pad *)pI; + p_pad = (struct hpi_control_cache_pad *)pC; if (!(p_pad->field_valid_flags & (1 << HPI_CTL_ATTR_INDEX(phm->u.c. @@ -531,7 +522,8 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n", found ? "Cached" : "Uncached", phm->adapter_index, - pI->control_index, pI->control_type, phm->u.c.attribute); + pC->u.i.control_index, pC->u.i.control_type, + phm->u.c.attribute); if (found) { phr->size = (u16)response_size; @@ -543,34 +535,36 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, return found; } -/** Updates the cache with Set values. - -Only update if no error. -Volume and Level return the limited values in the response, so use these -Multiplexer does so use sent values -*/ -void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, +short hpi_check_control_cache(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { - struct hpi_control_cache_single *pC; struct hpi_control_cache_info *pI; - if (phr->error) - return; - if (!find_control(phm->obj_index, p_cache, &pI)) { HPI_DEBUG_LOG(VERBOSE, "HPICMN find_control() failed for adap %d\n", phm->adapter_index); - return; + return 0; } - /* pC is the default cached control strucure. - May be cast to something else in the following switch statement. - */ - pC = (struct hpi_control_cache_single *)pI; + phr->error = 0; + phr->specific_error = 0; + phr->version = 0; + + return hpi_check_control_cache_single((struct hpi_control_cache_single + *)pI, phm, phr); +} + +/** Updates the cache with Set values. - switch (pI->control_type) { +Only update if no error. +Volume and Level return the limited values in the response, so use these +Multiplexer does so use sent values +*/ +void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single + *pC, struct hpi_message *phm, struct hpi_response *phr) +{ + switch (pC->u.i.control_type) { case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; @@ -625,6 +619,30 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, } } +void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_control_cache_single *pC; + struct hpi_control_cache_info *pI; + + if (phr->error) + return; + + if (!find_control(phm->obj_index, p_cache, &pI)) { + HPI_DEBUG_LOG(VERBOSE, + "HPICMN find_control() failed for adap %d\n", + phm->adapter_index); + return; + } + + /* pC is the default cached control strucure. + May be cast to something else in the following switch statement. + */ + pC = (struct hpi_control_cache_single *)pI; + + hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr); +} + /** Allocate control cache. \return Cache pointer, or NULL if allocation fails. @@ -637,12 +655,13 @@ struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count, if (!p_cache) return NULL; - p_cache->p_info = kcalloc(control_count, sizeof(*p_cache->p_info), - GFP_KERNEL); + p_cache->p_info = + kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL); if (!p_cache->p_info) { kfree(p_cache); return NULL; } + p_cache->cache_size_in_bytes = size_in_bytes; p_cache->control_count = control_count; p_cache->p_cache = p_dsp_control_buffer; -- cgit v0.10.2 From f9a376c3f6d77e59d41350901b2bafbaf8791df0 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:53 +1300 Subject: ALSA: asihpi: Add support for stream interrupt. Some cards have a so-called low-latency mode, in which they present a single multichannel stream with no mixing or samplerate conversion. In this mode the card can generate an interrupt per internal processing block (typically 32 or 64 frames) Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index c069033..ae29f30 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1,6 +1,6 @@ /* * Asihpi soundcard - * Copyright (c) by AudioScience Inc + * Copyright (c) by AudioScience Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -124,6 +124,16 @@ struct snd_card_asihpi { struct pci_dev *pci; struct hpi_adapter *hpi; + /* In low latency mode there is only one stream, a pointer to its + * private data is stored here on trigger and cleared on stop. + * The interrupt handler uses it as a parameter when calling + * snd_card_asihpi_timer_function(). + */ + struct snd_card_asihpi_pcm *llmode_streampriv; + struct tasklet_struct t; + void (*pcm_start)(struct snd_pcm_substream *substream); + void (*pcm_stop)(struct snd_pcm_substream *substream); + u32 h_mixer; struct clk_cache cc; @@ -544,6 +554,48 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) del_timer(&dpcm->timer); } +static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) +{ + struct snd_card_asihpi_pcm *dpcm; + struct snd_card_asihpi *card; + + BUG_ON(!substream); + + dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; + card = snd_pcm_substream_chip(substream); + + BUG_ON(in_interrupt()); + tasklet_disable(&card->t); + card->llmode_streampriv = dpcm; + tasklet_enable(&card->t); + + hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, + card->update_interval_frames, 0)); +} + +static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) +{ + struct snd_card_asihpi_pcm *dpcm; + struct snd_card_asihpi *card; + + BUG_ON(!substream); + + dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; + card = snd_pcm_substream_chip(substream); + + hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); + + if (in_interrupt()) + card->llmode_streampriv = NULL; + else { + tasklet_disable(&card->t); + card->llmode_streampriv = NULL; + tasklet_enable(&card->t); + } +} + static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -602,7 +654,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; } /* start the master stream */ - snd_card_asihpi_pcm_timer_start(substream); + card->pcm_start(substream); if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || !card->can_dma) hpi_handle_error(hpi_stream_start(dpcm->h_stream)); @@ -610,7 +662,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: snd_printdd("%s trigger stop\n", name); - snd_card_asihpi_pcm_timer_stop(substream); + card->pcm_stop(substream); snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) continue; @@ -641,12 +693,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_printdd("%s trigger pause release\n", name); + card->pcm_start(substream); hpi_handle_error(hpi_stream_start(dpcm->h_stream)); - snd_card_asihpi_pcm_timer_start(substream); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printdd("%s trigger pause push\n", name); - snd_card_asihpi_pcm_timer_stop(substream); + card->pcm_stop(substream); hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; default: @@ -718,8 +770,8 @@ static void snd_card_asihpi_timer_function(unsigned long data) u32 buffer_size, bytes_avail, samples_played, on_card_bytes; char name[16]; - snd_pcm_debug_name(substream, name, sizeof(name)); + snd_pcm_debug_name(substream, name, sizeof(name)); /* find minimum newdata and buffer pos in group */ snd_pcm_group_for_each_entry(s, substream) { @@ -779,7 +831,8 @@ static void snd_card_asihpi_timer_function(unsigned long data) newdata); } - snd_printddd("timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", + snd_printddd( + "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", name, s->number, state, ds->pcm_buf_elapsed_dma_ofs, ds->pcm_buf_host_rw_ofs, @@ -815,11 +868,13 @@ static void snd_card_asihpi_timer_function(unsigned long data) snd_pcm_group_for_each_entry(s, substream) { struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + runtime = s->runtime; /* don't link Cap and Play */ if (substream->stream != s->stream) continue; + /* Store dma offset for use by pointer callback */ ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; if (xfercount && @@ -878,16 +933,38 @@ static void snd_card_asihpi_timer_function(unsigned long data) pd, xfer2)); } } + /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */ ds->pcm_buf_host_rw_ofs += xfercount; ds->pcm_buf_elapsed_dma_ofs += xfercount; snd_pcm_period_elapsed(s); } } - if (dpcm->respawn_timer) + if (!card->hpi->interrupt_mode && dpcm->respawn_timer) add_timer(&dpcm->timer); } +static void snd_card_asihpi_int_task(unsigned long data) +{ + struct hpi_adapter *a = (struct hpi_adapter *)data; + struct snd_card_asihpi *asihpi; + + WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); + asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; + if (asihpi->llmode_streampriv) + snd_card_asihpi_timer_function( + (unsigned long)asihpi->llmode_streampriv); +} + +static void snd_card_asihpi_isr(struct hpi_adapter *a) +{ + struct snd_card_asihpi *asihpi; + + WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); + asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; + tasklet_schedule(&asihpi->t); +} + /***************************** PLAYBACK OPS ****************/ static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) @@ -995,13 +1072,22 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) runtime->private_free = snd_card_asihpi_runtime_free; memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback)); - snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; - snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; - /*?snd_card_asihpi_playback.period_bytes_min = - card->out_max_chans * 4096; */ - snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; - snd_card_asihpi_playback.periods_min = PERIODS_MIN; - snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + if (!card->hpi->interrupt_mode) { + snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; + snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_playback.periods_min = PERIODS_MIN; + snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + } else { + size_t pbmin = card->update_interval_frames * + card->out_max_chans; + snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_playback.period_bytes_min = pbmin; + snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_playback.periods_min = PERIODS_MIN; + snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin; + } + /* snd_card_asihpi_playback.fifo_size = 0; */ snd_card_asihpi_playback.channels_max = card->out_max_chans; snd_card_asihpi_playback.channels_min = card->out_min_chans; @@ -1036,7 +1122,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) card->update_interval_frames); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - card->update_interval_frames * 2, UINT_MAX); + card->update_interval_frames, UINT_MAX); snd_printdd("playback open\n"); @@ -1102,8 +1188,6 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) return 0; } - - static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, u32 h_stream) { @@ -1170,11 +1254,21 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) runtime->private_free = snd_card_asihpi_runtime_free; memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture)); - snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; - snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; - snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; - snd_card_asihpi_capture.periods_min = PERIODS_MIN; - snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + if (!card->hpi->interrupt_mode) { + snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; + snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_capture.periods_min = PERIODS_MIN; + snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + } else { + size_t pbmin = card->update_interval_frames * + card->out_max_chans; + snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_capture.period_bytes_min = pbmin; + snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_capture.periods_min = PERIODS_MIN; + snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin; + } /* snd_card_asihpi_capture.fifo_size = 0; */ snd_card_asihpi_capture.channels_max = card->in_max_chans; snd_card_asihpi_capture.channels_min = card->in_min_chans; @@ -1199,7 +1293,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - card->update_interval_frames * 2, UINT_MAX); + card->update_interval_frames, UINT_MAX); snd_pcm_set_sync(substream); @@ -2444,15 +2538,19 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, struct hpi_control *hpi_ctl) { - struct snd_card *card = asihpi->card; + struct snd_card *card; struct snd_kcontrol_new snd_control; - struct clk_cache *clkcache = &asihpi->cc; + struct clk_cache *clkcache; u32 hSC = hpi_ctl->h_control; int has_aes_in = 0; int i, j; u16 source; + if (snd_BUG_ON(!asihpi)) + return -EINVAL; + card = asihpi->card; + clkcache = &asihpi->cc; snd_control.private_value = hpi_ctl->h_control; clkcache->has_local = 0; @@ -2808,6 +2906,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->card = card; asihpi->pci = pci_dev; asihpi->hpi = hpi; + hpi->snd_card = card; snd_printk(KERN_INFO "adapter ID=%4X index=%d\n", asihpi->hpi->adapter->type, adapter_index); @@ -2830,8 +2929,16 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, if (err) asihpi->update_interval_frames = 512; - if (!asihpi->can_dma) - asihpi->update_interval_frames *= 2; + if (hpi->interrupt_mode) { + asihpi->pcm_start = snd_card_asihpi_pcm_int_start; + asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; + tasklet_init(&asihpi->t, snd_card_asihpi_int_task, + (unsigned long)hpi); + hpi->interrupt_callback = snd_card_asihpi_isr; + } else { + asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; + asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop; + } hpi_handle_error(hpi_instream_open(adapter_index, 0, &h_stream)); @@ -2841,6 +2948,9 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, hpi_handle_error(hpi_instream_close(h_stream)); + if (!asihpi->can_dma) + asihpi->update_interval_frames *= 2; + err = hpi_adapter_get_property(adapter_index, HPI_ADAPTER_PROPERTY_CURCHANNELS, &asihpi->in_max_chans, &asihpi->out_max_chans); @@ -2900,7 +3010,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, err = snd_card_register(card); if (!err) { - hpi->snd_card = card; dev++; return 0; } @@ -2914,6 +3023,16 @@ __nodev: static void snd_asihpi_remove(struct pci_dev *pci_dev) { struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); + struct snd_card_asihpi *asihpi = hpi->snd_card->private_data; + + /* Stop interrupts */ + if (hpi->interrupt_mode) { + hpi->interrupt_callback = NULL; + hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); + tasklet_kill(&asihpi->t); + } + snd_card_free(hpi->snd_card); hpi->snd_card = NULL; asihpi_adapter_remove(pci_dev); diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 4f28738..8d5abfa 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -163,6 +163,9 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, static void delete_adapter_obj(struct hpi_adapter_obj *pao); +static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao, + u32 message); + static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr); @@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao, case HPI_ADAPTER_DELETE: adapter_delete(pao, phm, phr); break; - default: hw_message(pao, phm, phr); break; @@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); + pao->irq_query_and_clear = adapter_irq_query_and_clear; + pao->instream_host_buffer_status = + phw->p_interface_buffer->instream_host_buffer_status; + pao->outstream_host_buffer_status = + phw->p_interface_buffer->outstream_host_buffer_status; + return hpi_add_adapter(pao); } @@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) /*****************************************************************************/ /* Adapter functions */ +static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao, + u32 message) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 hsr = 0; + + hsr = ioread32(phw->prHSR); + if (hsr & C6205_HSR_INTSRC) { + /* reset the interrupt from the DSP */ + iowrite32(C6205_HSR_INTSRC, phw->prHSR); + return HPI_IRQ_MIXER; + } + + return HPI_IRQ_NONE; +} /*****************************************************************************/ /* OutStream Host buffer functions */ @@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, if (boot_code_id[1] != 0) { /* DSP 1 is a C6713 */ /* CLKX0 <- '1' release the C6205 bootmode pulldowns */ - boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202); + boot_loader_write_mem32(pao, 0, 0x018C0024, 0x00002202); hpios_delay_micro_seconds(100); /* Reset the 6713 #1 - revB */ boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0); - - /* dummy read every 4 words for 6205 advisory 1.4.4 */ - boot_loader_read_mem32(pao, 0, 0); - + /* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */ + if (0 != (boot_loader_read_mem32(pao, 0, + (C6205_BAR0_TIMER1_CTL)) & ~8)) + return HPI6205_ERROR_6205_REG; hpios_delay_micro_seconds(100); + /* Release C6713 from reset - revB */ boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4); + if (4 != (boot_loader_read_mem32(pao, 0, + (C6205_BAR0_TIMER1_CTL)) & ~8)) + return HPI6205_ERROR_6205_REG; hpios_delay_micro_seconds(100); } @@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, return 0; } - /* Assume buffer of type struct bus_master_interface + /* Assume buffer of type struct bus_master_interface_62 is allocated "noncacheable" */ if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index c9bdc28..48380ce 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -686,8 +686,8 @@ union hpi_adapterx_msg { u16 value; } test_assert; struct { - u32 yes; - } irq_query; + u32 message; + } irq; u32 pad[3]; }; diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h index e441212..46629c2 100644 --- a/sound/pci/asihpi/hpicmn.h +++ b/sound/pci/asihpi/hpicmn.h @@ -1,7 +1,7 @@ /** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -21,7 +21,11 @@ struct hpi_adapter_obj; /* a function that takes an adapter obj and returns an int */ -typedef int adapter_int_func(struct hpi_adapter_obj *pao); +typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message); + +#define HPI_IRQ_NONE (0) +#define HPI_IRQ_MESSAGE (1) +#define HPI_IRQ_MIXER (2) struct hpi_adapter_obj { struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */ @@ -33,6 +37,9 @@ struct hpi_adapter_obj { u16 dsp_crashed; u16 has_control_cache; void *priv; + adapter_int_func *irq_query_and_clear; + struct hpi_hostbuffer_status *instream_host_buffer_status; + struct hpi_hostbuffer_status *outstream_host_buffer_status; }; struct hpi_control_cache { @@ -55,13 +62,21 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao); short hpi_check_control_cache(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); + +short hpi_check_control_cache_single(struct hpi_control_cache_single *pC, + struct hpi_message *phm, struct hpi_response *phr); + struct hpi_control_cache *hpi_alloc_control_cache(const u32 number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer); + void hpi_free_control_cache(struct hpi_control_cache *p_cache); void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); +void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single + *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 7f02720..9454932 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -1,7 +1,8 @@ /******************************************************************************* - AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Common Linux HPI ioctl and module probe/remove functions + + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -12,11 +13,6 @@ 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 - -Common Linux HPI ioctl and module probe/remove functions *******************************************************************************/ #define SOURCEFILE_NAME "hpioctl.c" @@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions #include "hpicmn.h" #include +#include #include #include #include @@ -307,10 +304,38 @@ out: return err; } +static int asihpi_irq_count; + +static irqreturn_t asihpi_isr(int irq, void *dev_id) +{ + struct hpi_adapter *a = dev_id; + int handled; + + if (!a->adapter->irq_query_and_clear) { + pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type, + a->adapter->index); + return IRQ_NONE; + } + + handled = a->adapter->irq_query_and_clear(a->adapter, 0); + + if (!handled) + return IRQ_NONE; + + asihpi_irq_count++; + /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n", + asihpi_irq_count, a->adapter->type, a->adapter->index); */ + + if (a->interrupt_callback) + a->interrupt_callback(a); + + return IRQ_HANDLED; +} + int asihpi_adapter_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { - int idx, nm; + int idx, nm, low_latency_mode = 0, irq_supported = 0; int adapter_index; unsigned int memlen; struct hpi_message hm; @@ -388,8 +413,39 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, hm.adapter_index = adapter.adapter->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - if (hr.error) + if (hr.error) { + HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n"); + goto err; + } + + /* Check if current mode == Low Latency mode */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_MODE); + hm.adapter_index = adapter.adapter->index; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + + if (hr.error) { + HPI_DEBUG_LOG(ERROR, + "HPI_ADAPTER_GET_MODE failed, aborting\n"); goto err; + } + + if (hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) + low_latency_mode = 1; + + /* Check if IRQs are supported */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_PROPERTY); + hm.adapter_index = adapter.adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + if (hr.error || !hr.u.ax.property_get.parameter1) { + dev_info(&pci_dev->dev, + "IRQs not supported by adapter at index %d\n", + adapter.adapter->index); + } else { + irq_supported = 1; + } /* WARNING can't init mutex in 'adapter' * and then copy it to adapters[] ?!?! @@ -398,6 +454,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, mutex_init(&adapters[adapter_index].mutex); pci_set_drvdata(pci_dev, &adapters[adapter_index]); + if (low_latency_mode && irq_supported) { + if (!adapter.adapter->irq_query_and_clear) { + dev_err(&pci_dev->dev, + "no IRQ handler for adapter %d, aborting\n", + adapter.adapter->index); + goto err; + } + + /* Disable IRQ generation on DSP side by setting the rate to 0 */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_PROPERTY); + hm.adapter_index = adapter.adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; + hm.u.ax.property_set.parameter1 = 0; + hm.u.ax.property_set.parameter2 = 0; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + if (hr.error) { + HPI_DEBUG_LOG(ERROR, + "HPI_ADAPTER_GET_MODE failed, aborting\n"); + goto err; + } + + /* Note: request_irq calls asihpi_isr here */ + if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED, + "asihpi", &adapters[adapter_index])) { + dev_err(&pci_dev->dev, "request_irq(%d) failed\n", + pci_dev->irq); + goto err; + } + + adapters[adapter_index].interrupt_mode = 1; + + dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq); + adapters[adapter_index].irq = pci_dev->irq; + } else { + dev_info(&pci_dev->dev, "using polled mode\n"); + } + dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n", adapter.adapter->type, adapter_index); @@ -431,6 +525,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) pa = pci_get_drvdata(pci_dev); pci = pa->adapter->pci; + /* Disable IRQ generation on DSP side */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_PROPERTY); + hm.adapter_index = pa->adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; + hm.u.ax.property_set.parameter1 = 0; + hm.u.ax.property_set.parameter2 = 0; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_DELETE); hm.adapter_index = pa->adapter->index; @@ -442,6 +545,9 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) iounmap(pci.ap_mem_base[idx]); } + if (pa->irq) + free_irq(pa->irq, pa); + if (pa->p_buffer) vfree(pa->p_buffer); diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index d17d017..4e38360 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -151,6 +151,10 @@ struct hpi_adapter { struct hpi_adapter_obj *adapter; struct snd_card *snd_card; + int irq; + int interrupt_mode; + void (*interrupt_callback) (struct hpi_adapter *); + /* mutex prevents contention for one card between multiple user programs (via ioctl) */ struct mutex mutex; -- cgit v0.10.2 From 5bc91f5b3c732bdb3b9e7cc8bd27969d25015bcd Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:54 +1300 Subject: ALSA: asihpi: Turn off msg/resp logging after DSP has crashed. Prevents spewing of useless messages if app keeps trying to access the card. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index d4790dd..736f453 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -35,6 +35,7 @@ static struct pci_device_id asihpi_pci_tbl[] = { static struct hpios_spinlock msgx_lock; static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS]; +static int logging_enabled = 1; static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci *pci_info) @@ -312,7 +313,9 @@ static void instream_message(struct hpi_message *phm, void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, void *h_owner) { - HPI_DEBUG_MESSAGE(DEBUG, phm); + + if (logging_enabled) + HPI_DEBUG_MESSAGE(DEBUG, phm); if (phm->type != HPI_TYPE_REQUEST) { hpi_init_response(phr, phm->object, phm->function, @@ -352,8 +355,14 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, hw_entry_point(phm, phr); break; } - HPI_DEBUG_RESPONSE(phr); + if (logging_enabled) + HPI_DEBUG_RESPONSE(phr); + + if (phr->error >= HPI_ERROR_DSP_COMMUNICATION) { + hpi_debug_level_set(HPI_DEBUG_LEVEL_ERROR); + logging_enabled = 0; + } } static void adapter_open(struct hpi_message *phm, struct hpi_response *phr) -- cgit v0.10.2 From 12eb0898741870882ca474708e811983d5a5d768 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:55 +1300 Subject: ALSA: asihpi: Use standard printk helpers Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index ae29f30..e9273fb 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -380,7 +380,7 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (err) { - snd_printk(KERN_ERR + dev_err(&asihpi->pci->dev, "No local sampleclock, err %d\n", err); } @@ -1438,7 +1438,7 @@ static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, if (err < 0) return err; else if (mixer_dump) - snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index); + dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index); return 0; } @@ -2652,7 +2652,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (err) { if (err == HPI_ERROR_CONTROL_DISABLED) { if (mixer_dump) - snd_printk(KERN_INFO + dev_info(&asihpi->pci->dev, "Disabled HPI Control(%d)\n", idx); continue; @@ -2717,9 +2717,8 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) case HPI_CONTROL_COMPANDER: default: if (mixer_dump) - snd_printk(KERN_INFO - "Untranslated HPI Control" - "(%d) %d %d %d %d %d\n", + dev_info(&asihpi->pci->dev, + "Untranslated HPI Control (%d) %d %d %d %d %d\n", idx, hpi_ctl.control_type, hpi_ctl.src_node_type, @@ -2734,7 +2733,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (HPI_ERROR_INVALID_OBJ_INDEX != err) hpi_handle_error(err); - snd_printk(KERN_INFO "%d mixer controls found\n", idx); + dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx); return 0; } @@ -2897,8 +2896,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, &card); if (err < 0) return err; - snd_printk(KERN_WARNING - "**** WARNING **** Adapter index %d->ALSA index %d\n", + dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n", adapter_index, card->number); } @@ -2908,9 +2906,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->hpi = hpi; hpi->snd_card = card; - snd_printk(KERN_INFO "adapter ID=%4X index=%d\n", - asihpi->hpi->adapter->type, adapter_index); - err = hpi_adapter_get_property(adapter_index, HPI_ADAPTER_PROPERTY_CAPS1, NULL, &asihpi->support_grouping); @@ -2968,22 +2963,21 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->in_min_chans = 1; } - snd_printk(KERN_INFO "update_interval_frames: %d", - asihpi->update_interval_frames); - snd_printk(KERN_INFO "Has dma:%d, grouping:%d, mrx:%d\n", + dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n", asihpi->can_dma, asihpi->support_grouping, - asihpi->support_mrx + asihpi->support_mrx, + asihpi->update_interval_frames ); err = snd_card_asihpi_pcm_new(asihpi, 0); if (err < 0) { - snd_printk(KERN_ERR "pcm_new failed\n"); + dev_err(&pci_dev->dev, "pcm_new failed\n"); goto __nodev; } err = snd_card_asihpi_mixer_new(asihpi); if (err < 0) { - snd_printk(KERN_ERR "mixer_new failed\n"); + dev_err(&pci_dev->dev, "mixer_new failed\n"); goto __nodev; } @@ -3015,7 +3009,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, } __nodev: snd_card_free(card); - snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err); + dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err); return err; } -- cgit v0.10.2 From dc612838eac746b11bb4e5d923dafeea0ba7e81b Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:56 +1300 Subject: ALSA: asihpi: don't fail probe if adapter mode read fails Only determining if low latency mode is enabled. Failure indicates adapter has no modes Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 9454932..e457eb8 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -424,14 +424,13 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, hm.adapter_index = adapter.adapter->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - if (hr.error) { - HPI_DEBUG_LOG(ERROR, - "HPI_ADAPTER_GET_MODE failed, aborting\n"); - goto err; - } - - if (hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) + if (!hr.error + && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) low_latency_mode = 1; + else + dev_info(&pci_dev->dev, + "Adapter at index %d is not in low latency mode\n", + adapter.adapter->index); /* Check if IRQs are supported */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, -- cgit v0.10.2 From 51e6f47dd2e3463dac6f37128fd7b7cb40c500de Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Thu, 20 Nov 2014 16:22:57 +1300 Subject: ALSA: asihpi: used parts of message/response are zeroed before use Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c index 032d563..7eb6171 100644 --- a/sound/pci/asihpi/hpimsginit.c +++ b/sound/pci/asihpi/hpimsginit.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. + Copyright (C) 1997-2014 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -37,11 +37,15 @@ static u16 gwSSX2_bypass; static void hpi_init_message(struct hpi_message *phm, u16 object, u16 function) { - memset(phm, 0, sizeof(*phm)); + u16 size; + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) - phm->size = msg_size[object]; + size = msg_size[object]; else - phm->size = sizeof(*phm); + size = sizeof(*phm); + + memset(phm, 0, size); + phm->size = size; if (gwSSX2_bypass) phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE; @@ -60,12 +64,16 @@ static void hpi_init_message(struct hpi_message *phm, u16 object, void hpi_init_response(struct hpi_response *phr, u16 object, u16 function, u16 error) { - memset(phr, 0, sizeof(*phr)); - phr->type = HPI_TYPE_RESPONSE; + u16 size; + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) - phr->size = res_size[object]; + size = res_size[object]; else - phr->size = sizeof(*phr); + size = sizeof(*phr); + + memset(phr, 0, sizeof(*phr)); + phr->size = size; + phr->type = HPI_TYPE_RESPONSE; phr->object = object; phr->function = function; phr->error = error; @@ -86,7 +94,7 @@ void hpi_init_message_response(struct hpi_message *phm, static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, u16 object, u16 function) { - memset(phm, 0, sizeof(*phm)); + memset(phm, 0, size); if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) { phm->size = size; phm->type = HPI_TYPE_REQUEST; @@ -100,7 +108,9 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, void hpi_init_responseV1(struct hpi_response_header *phr, u16 size, u16 object, u16 function) { - memset(phr, 0, sizeof(*phr)); + (void)object; + (void)function; + memset(phr, 0, size); phr->size = size; phr->version = 1; phr->type = HPI_TYPE_RESPONSE; -- cgit v0.10.2 From 4aab3b5b3ccf94fc907e66233e6ca4d8675759a6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 22 Nov 2014 09:22:42 -0500 Subject: percpu-ref: fix DEAD flag contamination of percpu pointer While decoupling ATOMIC and DEAD flags, f47ad4578461 ("percpu_ref: decouple switching to percpu mode and reinit") updated __ref_is_percpu() so that it only tests ATOMIC flag to determine whether the ref is in percpu mode or not; however, while DEAD implies ATOMIC, the two flags are set separately during percpu_ref_kill() and if __ref_is_percpu() races percpu_ref_kill(), it may see DEAD w/o ATOMIC. Because __ref_is_percpu() returns @ref->percpu_count_ptr value verbatim as the percpu pointer after testing ATOMIC, the pointer may now be contaminated with the DEAD flag. This can be fixed by clearing the flag bits before returning the pointer which was the fix proposed by Shaohua; however, as DEAD implies ATOMIC, we can just test for both flags at once and avoid the explicit masking. Update __ref_is_percpu() so that it tests that both ATOMIC and DEAD are clear before returning @ref->percpu_count_ptr as the percpu pointer. Signed-off-by: Tejun Heo Reported-and-Reviewed-by: Shaohua Li Link: http://lkml.kernel.org/r/995deb699f5b873c45d667df4add3b06f73c2c25.1416638887.git.shli@kernel.org Fixes: f47ad4578461 ("percpu_ref: decouple switching to percpu mode and reinit") diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index d5c89e0..51ce60c 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -133,7 +133,13 @@ static inline bool __ref_is_percpu(struct percpu_ref *ref, /* paired with smp_store_release() in percpu_ref_reinit() */ smp_read_barrier_depends(); - if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC)) + /* + * Theoretically, the following could test just ATOMIC; however, + * then we'd have to mask off DEAD separately as DEAD may be + * visible without ATOMIC if we race with percpu_ref_kill(). DEAD + * implies ATOMIC anyway. Test them together. + */ + if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC_DEAD)) return false; *percpu_countp = (unsigned long __percpu *)percpu_ptr; -- cgit v0.10.2 From b6fef4c6b8c1994ffe050dcdb9391427bf0d9882 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 21 Nov 2014 19:37:09 -0800 Subject: ipv6: Do not treat a GSO_TCPV4 request from UDP tunnel over IPv6 as invalid This patch adds SKB_GSO_TCPV4 to the list of supported GSO types handled by the IPv6 GSO offloads. Without this change VXLAN tunnels running over IPv6 do not currently handle IPv4 TCP TSO requests correctly and end up handing the non-segmented frame off to the device. Below is the before and after for a simple netperf TCP_STREAM test between two endpoints tunneling IPv4 over a VXLAN tunnel running on IPv6 on top of a 1Gb/s network adapter. Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.29 0.88 Before 87380 16384 16384 10.03 895.69 After Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index a071563..01e12d0 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -69,7 +69,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int nhoff; if (unlikely(skb_shinfo(skb)->gso_type & - ~(SKB_GSO_UDP | + ~(SKB_GSO_TCPV4 | + SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | -- cgit v0.10.2 From 4556dc591691fca743518edb24f15fbc83b5c8ef Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 21 Nov 2014 23:52:52 -0800 Subject: ixgbe: Correctly disable VLAN filter in promiscuous mode IXGBE adapter seems to require that VLAN filtering be enabled if VMDQ or SRIOV are enabled. When those functions are disabled, VLAN filtering may be disabled in promiscuous mode. Prior to commit a9b8943ee129 ("ixgbe: remove vlan_filter_disable and enable functions") The logic was correct. However, after the commit the logic got reversed and VLAN filtered in now turned on when VMDQ/SRIOV is disabled. This patch changes the condition to enable hw vlan filtered when VMDQ or SRIOV is enabled. Fixes: a9b8943ee129 ("ixgbe: remove vlan_filter_disable and enable functions") Cc: stable CC: Jacob Keller Signed-off-by: Vladislav Yasevich Acked-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d2df4e3..7acde46 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3936,8 +3936,8 @@ void ixgbe_set_rx_mode(struct net_device *netdev) * if SR-IOV and VMDQ are disabled - otherwise ensure * that hardware VLAN filters remain enabled. */ - if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED | - IXGBE_FLAG_SRIOV_ENABLED))) + if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED | + IXGBE_FLAG_SRIOV_ENABLED)) vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN); } else { if (netdev->flags & IFF_ALLMULTI) { -- cgit v0.10.2 From b5b2ffc0574e1f271d79b6b992ee382dc9d5eaa8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 21 Nov 2014 23:52:53 -0800 Subject: ixgbe: fix use after free adapter->state test in ixgbe_remove/ixgbe_probe While working on a different issue, I noticed an annoying use after free bug on my machine when unloading the ixgbe driver: [ 8642.318797] ixgbe 0000:02:00.1: removed PHC on p2p2 [ 8642.742716] ixgbe 0000:02:00.1: complete [ 8642.743784] BUG: unable to handle kernel paging request at ffff8807d3740a90 [ 8642.744828] IP: [] ixgbe_remove+0xfc/0x1b0 [ixgbe] [ 8642.745886] PGD 20c6067 PUD 81c1f6067 PMD 81c15a067 PTE 80000007d3740060 [ 8642.746956] Oops: 0002 [#1] SMP DEBUG_PAGEALLOC [ 8642.748039] Modules linked in: [...] [ 8642.752929] CPU: 1 PID: 1225 Comm: rmmod Not tainted 3.18.0-rc2+ #49 [ 8642.754203] Hardware name: Supermicro X10SLM-F/X10SLM-F, BIOS 1.1b 11/01/2013 [ 8642.755505] task: ffff8807e34d3fe0 ti: ffff8807b7204000 task.ti: ffff8807b7204000 [ 8642.756831] RIP: 0010:[] [] ixgbe_remove+0xfc/0x1b0 [ixgbe] [...] [ 8642.774335] Stack: [ 8642.775805] ffff8807ee824098 ffff8807ee824098 ffffffffa01f3000 ffff8807ee824000 [ 8642.777326] ffff8807b7207e18 ffffffff8137720f ffff8807ee824098 ffff8807ee824098 [ 8642.778848] ffffffffa01f3068 ffff8807ee8240f8 ffff8807b7207e38 ffffffff8144180f [ 8642.780365] Call Trace: [ 8642.781869] [] pci_device_remove+0x3f/0xc0 [ 8642.783395] [] __device_release_driver+0x7f/0xf0 [ 8642.784876] [] driver_detach+0xb8/0xc0 [ 8642.786352] [] bus_remove_driver+0x59/0xe0 [ 8642.787783] [] driver_unregister+0x30/0x70 [ 8642.789202] [] pci_unregister_driver+0x25/0xa0 [ 8642.790657] [] ixgbe_exit_module+0x1c/0xc8e [ixgbe] [ 8642.792064] [] SyS_delete_module+0x132/0x1c0 [ 8642.793450] [] ? do_notify_resume+0x61/0xa0 [ 8642.794837] [] system_call_fastpath+0x12/0x17 The issue is that test_and_set_bit() done on adapter->state is being performed *after* the netdevice has been freed via free_netdev(). When netdev is being allocated on initialization time, it allocates a private area, here struct ixgbe_adapter, that resides after the net_device structure. In ixgbe_probe(), the device init routine, we set up the adapter after alloc_etherdev_mq() on the private area and add a reference for the pci_dev as well via pci_set_drvdata(). Both in the error path of ixgbe_probe(), but also on module unload when ixgbe_remove() is being called, commit 41c62843eb6a ("ixgbe: Fix rcu warnings induced by LER") accesses adapter after free_netdev(). The patch stores the result in a bool and thus fixes above oops on my side. Fixes: 41c62843eb6a ("ixgbe: Fix rcu warnings induced by LER") Cc: stable Cc: Mark Rustad Signed-off-by: Daniel Borkmann Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 7acde46..82ffe8b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7979,6 +7979,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int i, err, pci_using_dac, expected_gts; unsigned int indices = MAX_TX_QUEUES; u8 part_str[IXGBE_PBANUM_LENGTH]; + bool disable_dev = false; #ifdef IXGBE_FCOE u16 device_caps; #endif @@ -8369,13 +8370,14 @@ err_sw_init: iounmap(adapter->io_addr); kfree(adapter->mac_table); err_ioremap: + disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); err_alloc_etherdev: pci_release_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM)); err_pci_reg: err_dma: - if (!adapter || !test_and_set_bit(__IXGBE_DISABLED, &adapter->state)) + if (!adapter || disable_dev) pci_disable_device(pdev); return err; } @@ -8393,6 +8395,7 @@ static void ixgbe_remove(struct pci_dev *pdev) { struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; + bool disable_dev; ixgbe_dbg_adapter_exit(adapter); @@ -8442,11 +8445,12 @@ static void ixgbe_remove(struct pci_dev *pdev) e_dev_info("complete\n"); kfree(adapter->mac_table); + disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); pci_disable_pcie_error_reporting(pdev); - if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state)) + if (disable_dev) pci_disable_device(pdev); } -- cgit v0.10.2 From 17a402a0075c7848d940eb846f8db1da6a832c5d Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Fri, 21 Nov 2014 23:52:54 -0800 Subject: igb: Fixes needed for surprise removal support This patch adds some checks in order to prevent panic's on surprise removal of devices during S0, S3, S4. Without this patch, Thunderbolt type device removal will panic the system. Signed-off-by: Yanir Lubetkin Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index a2d72a8..487cd9c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1012,7 +1012,8 @@ static void igb_free_q_vector(struct igb_adapter *adapter, int v_idx) /* igb_get_stats64() might access the rings on this vector, * we must wait a grace period before freeing it. */ - kfree_rcu(q_vector, rcu); + if (q_vector) + kfree_rcu(q_vector, rcu); } /** @@ -1792,8 +1793,10 @@ void igb_down(struct igb_adapter *adapter) adapter->flags &= ~IGB_FLAG_NEED_LINK_UPDATE; for (i = 0; i < adapter->num_q_vectors; i++) { - napi_synchronize(&(adapter->q_vector[i]->napi)); - napi_disable(&(adapter->q_vector[i]->napi)); + if (adapter->q_vector[i]) { + napi_synchronize(&adapter->q_vector[i]->napi); + napi_disable(&adapter->q_vector[i]->napi); + } } @@ -3717,7 +3720,8 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_tx_queues; i++) - igb_free_tx_resources(adapter->tx_ring[i]); + if (adapter->tx_ring[i]) + igb_free_tx_resources(adapter->tx_ring[i]); } void igb_unmap_and_free_tx_resource(struct igb_ring *ring, @@ -3782,7 +3786,8 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_tx_queues; i++) - igb_clean_tx_ring(adapter->tx_ring[i]); + if (adapter->tx_ring[i]) + igb_clean_tx_ring(adapter->tx_ring[i]); } /** @@ -3819,7 +3824,8 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_rx_queues; i++) - igb_free_rx_resources(adapter->rx_ring[i]); + if (adapter->rx_ring[i]) + igb_free_rx_resources(adapter->rx_ring[i]); } /** @@ -3874,7 +3880,8 @@ static void igb_clean_all_rx_rings(struct igb_adapter *adapter) int i; for (i = 0; i < adapter->num_rx_queues; i++) - igb_clean_rx_ring(adapter->rx_ring[i]); + if (adapter->rx_ring[i]) + igb_clean_rx_ring(adapter->rx_ring[i]); } /** @@ -7404,6 +7411,8 @@ static int igb_resume(struct device *dev) pci_restore_state(pdev); pci_save_state(pdev); + if (!pci_device_is_present(pdev)) + return -ENODEV; err = pci_enable_device_mem(pdev); if (err) { dev_err(&pdev->dev, -- cgit v0.10.2 From 73112f9b08ddb32c5cdaccda61bd88dfe9baf8b2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 22 Nov 2014 15:39:17 +0100 Subject: solos-pci: fix error return code Return a negative error code on failure. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier ret; expression e1,e2; @@ ( if (\(ret < 0\|ret != 0\)) { ... return ret; } | ret = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 7652e8d..21b0bc6 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -1225,11 +1225,13 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE); if (!card->config_regs) { dev_warn(&dev->dev, "Failed to ioremap config registers\n"); + err = -ENOMEM; goto out_release_regions; } card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE); if (!card->buffers) { dev_warn(&dev->dev, "Failed to ioremap data buffers\n"); + err = -ENOMEM; goto out_unmap_config; } -- cgit v0.10.2 From 5ac6c72e594471acfa5b00210c51d533a73413ad Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 21 Oct 2014 16:12:18 +0300 Subject: iwlwifi: mvm: check TLV flag before trying to use hotspot firmware commands Older firmwares do not provide support for the HOT_SPOT_CMD command. Check for the appropriate TLV flag that declares hotspot support in the firmware to prevent a firmware assertion failure that can be triggered from the userspace, Cc: stable@vger.kernel.org [3.17+] Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 4f6e668..b894a84 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -155,6 +155,7 @@ enum iwl_ucode_tlv_api { * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), * which also implies support for the scheduler configuration command + * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command */ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), @@ -163,6 +164,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = BIT(10), IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = BIT(11), IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12), + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18), }; /* The default calibrate table size if not specified by firmware file */ diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index b624058..b6d2683 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -2448,9 +2448,15 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_STATION: - /* Use aux roc framework (HS20) */ - ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, - vif, duration); + if (mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) { + /* Use aux roc framework (HS20) */ + ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, + vif, duration); + goto out_unlock; + } + IWL_ERR(mvm, "hotspot not supported\n"); + ret = -EINVAL; goto out_unlock; case NL80211_IFTYPE_P2P_DEVICE: /* handle below */ -- cgit v0.10.2 From af726f21ed8af2cdaa4e93098dc211521218ae65 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 22 Nov 2014 18:00:31 -0800 Subject: x86_64, traps: Fix the espfix64 #DF fixup and rewrite it in C There's nothing special enough about the espfix64 double fault fixup to justify writing it in assembly. Move it to C. This also fixes a bug: if the double fault came from an IST stack, the old asm code would return to a partially uninitialized stack frame. Fixes: 3891a04aafd668686239349ea58f3314ea2af86b Signed-off-by: Andy Lutomirski Reviewed-by: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index df088bb..a4dc8de 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -828,6 +828,7 @@ ENTRY(native_iret) jnz native_irq_return_ldt #endif +.global native_irq_return_iret native_irq_return_iret: iretq _ASM_EXTABLE(native_irq_return_iret, bad_iret) @@ -922,37 +923,6 @@ ENTRY(retint_kernel) CFI_ENDPROC END(common_interrupt) - /* - * If IRET takes a fault on the espfix stack, then we - * end up promoting it to a doublefault. In that case, - * modify the stack to make it look like we just entered - * the #GP handler from user space, similar to bad_iret. - */ -#ifdef CONFIG_X86_ESPFIX64 - ALIGN -__do_double_fault: - XCPT_FRAME 1 RDI+8 - movq RSP(%rdi),%rax /* Trap on the espfix stack? */ - sarq $PGDIR_SHIFT,%rax - cmpl $ESPFIX_PGD_ENTRY,%eax - jne do_double_fault /* No, just deliver the fault */ - cmpl $__KERNEL_CS,CS(%rdi) - jne do_double_fault - movq RIP(%rdi),%rax - cmpq $native_irq_return_iret,%rax - jne do_double_fault /* This shouldn't happen... */ - movq PER_CPU_VAR(kernel_stack),%rax - subq $(6*8-KERNEL_STACK_OFFSET),%rax /* Reset to original stack */ - movq %rax,RSP(%rdi) - movq $0,(%rax) /* Missing (lost) #GP error code */ - movq $general_protection,RIP(%rdi) - retq - CFI_ENDPROC -END(__do_double_fault) -#else -# define __do_double_fault do_double_fault -#endif - /* * APIC interrupts. */ @@ -1124,7 +1094,7 @@ idtentry overflow do_overflow has_error_code=0 idtentry bounds do_bounds has_error_code=0 idtentry invalid_op do_invalid_op has_error_code=0 idtentry device_not_available do_device_not_available has_error_code=0 -idtentry double_fault __do_double_fault has_error_code=1 paranoid=1 +idtentry double_fault do_double_fault has_error_code=1 paranoid=1 idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0 idtentry invalid_TSS do_invalid_TSS has_error_code=1 idtentry segment_not_present do_segment_not_present has_error_code=1 diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 0d0e922..8196627 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -259,6 +259,30 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) static const char str[] = "double fault"; struct task_struct *tsk = current; +#ifdef CONFIG_X86_ESPFIX64 + extern unsigned char native_irq_return_iret[]; + + /* + * If IRET takes a non-IST fault on the espfix64 stack, then we + * end up promoting it to a doublefault. In that case, modify + * the stack to make it look like we just entered the #GP + * handler from user space, similar to bad_iret. + */ + if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY && + regs->cs == __KERNEL_CS && + regs->ip == (unsigned long)native_irq_return_iret) + { + struct pt_regs *normal_regs = task_pt_regs(current); + + /* Fake a #GP(0) from userspace. */ + memmove(&normal_regs->ip, (void *)regs->sp, 5*8); + normal_regs->orig_ax = 0; /* Missing (lost) #GP error code */ + regs->ip = (unsigned long)general_protection; + regs->sp = (unsigned long)&normal_regs->orig_ax; + return; + } +#endif + exception_enter(); /* Return not checked because double check cannot be ignored */ notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); -- cgit v0.10.2 From 6f442be2fb22be02cafa606f1769fa1e6f894441 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 22 Nov 2014 18:00:32 -0800 Subject: x86_64, traps: Stop using IST for #SS On a 32-bit kernel, this has no effect, since there are no IST stacks. On a 64-bit kernel, #SS can only happen in user code, on a failed iret to user space, a canonical violation on access via RSP or RBP, or a genuine stack segment violation in 32-bit kernel code. The first two cases don't need IST, and the latter two cases are unlikely fatal bugs, and promoting them to double faults would be fine. This fixes a bug in which the espfix64 code mishandles a stack segment violation. This saves 4k of memory per CPU and a tiny bit of code. Signed-off-by: Andy Lutomirski Reviewed-by: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/page_32_types.h b/arch/x86/include/asm/page_32_types.h index f48b17d..3a52ee0 100644 --- a/arch/x86/include/asm/page_32_types.h +++ b/arch/x86/include/asm/page_32_types.h @@ -20,7 +20,6 @@ #define THREAD_SIZE_ORDER 1 #define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) -#define STACKFAULT_STACK 0 #define DOUBLEFAULT_STACK 1 #define NMI_STACK 0 #define DEBUG_STACK 0 diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h index 6782051..75450b2 100644 --- a/arch/x86/include/asm/page_64_types.h +++ b/arch/x86/include/asm/page_64_types.h @@ -14,12 +14,11 @@ #define IRQ_STACK_ORDER 2 #define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER) -#define STACKFAULT_STACK 1 -#define DOUBLEFAULT_STACK 2 -#define NMI_STACK 3 -#define DEBUG_STACK 4 -#define MCE_STACK 5 -#define N_EXCEPTION_STACKS 5 /* hw limit: 7 */ +#define DOUBLEFAULT_STACK 1 +#define NMI_STACK 2 +#define DEBUG_STACK 3 +#define MCE_STACK 4 +#define N_EXCEPTION_STACKS 4 /* hw limit: 7 */ #define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT) #define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1)) diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index bc8352e..707adc6 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -39,6 +39,7 @@ asmlinkage void simd_coprocessor_error(void); #ifdef CONFIG_TRACING asmlinkage void trace_page_fault(void); +#define trace_stack_segment stack_segment #define trace_divide_error divide_error #define trace_bounds bounds #define trace_invalid_op invalid_op diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index 1abcb50..ff86f19 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c @@ -24,7 +24,6 @@ static char x86_stack_ids[][8] = { [ DEBUG_STACK-1 ] = "#DB", [ NMI_STACK-1 ] = "NMI", [ DOUBLEFAULT_STACK-1 ] = "#DF", - [ STACKFAULT_STACK-1 ] = "#SS", [ MCE_STACK-1 ] = "#MC", #if DEBUG_STKSZ > EXCEPTION_STKSZ [ N_EXCEPTION_STACKS ... diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index a4dc8de..49a0c17 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1259,7 +1259,7 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK -idtentry stack_segment do_stack_segment has_error_code=1 paranoid=1 +idtentry stack_segment do_stack_segment has_error_code=1 #ifdef CONFIG_XEN idtentry xen_debug do_debug has_error_code=0 idtentry xen_int3 do_int3 has_error_code=0 diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 8196627..48035e9 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -233,27 +233,11 @@ DO_ERROR(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op) DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun",coprocessor_segment_overrun) DO_ERROR(X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS) DO_ERROR(X86_TRAP_NP, SIGBUS, "segment not present", segment_not_present) -#ifdef CONFIG_X86_32 DO_ERROR(X86_TRAP_SS, SIGBUS, "stack segment", stack_segment) -#endif DO_ERROR(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check) #ifdef CONFIG_X86_64 /* Runs on IST stack */ -dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) -{ - enum ctx_state prev_state; - - prev_state = exception_enter(); - if (notify_die(DIE_TRAP, "stack segment", regs, error_code, - X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) { - preempt_conditional_sti(regs); - do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); - preempt_conditional_cli(regs); - } - exception_exit(prev_state); -} - dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) { static const char str[] = "double fault"; @@ -802,7 +786,7 @@ void __init trap_init(void) set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun); set_intr_gate(X86_TRAP_TS, invalid_TSS); set_intr_gate(X86_TRAP_NP, segment_not_present); - set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK); + set_intr_gate(X86_TRAP_SS, stack_segment); set_intr_gate(X86_TRAP_GP, general_protection); set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug); set_intr_gate(X86_TRAP_MF, coprocessor_error); -- cgit v0.10.2 From b645af2d5905c4e32399005b867987919cbfc3ae Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Sat, 22 Nov 2014 18:00:33 -0800 Subject: x86_64, traps: Rework bad_iret It's possible for iretq to userspace to fail. This can happen because of a bad CS, SS, or RIP. Historically, we've handled it by fixing up an exception from iretq to land at bad_iret, which pretends that the failed iret frame was really the hardware part of #GP(0) from userspace. To make this work, there's an extra fixup to fudge the gs base into a usable state. This is suboptimal because it loses the original exception. It's also buggy because there's no guarantee that we were on the kernel stack to begin with. For example, if the failing iret happened on return from an NMI, then we'll end up executing general_protection on the NMI stack. This is bad for several reasons, the most immediate of which is that general_protection, as a non-paranoid idtentry, will try to deliver signals and/or schedule from the wrong stack. This patch throws out bad_iret entirely. As a replacement, it augments the existing swapgs fudge into a full-blown iret fixup, mostly written in C. It's should be clearer and more correct. Signed-off-by: Andy Lutomirski Reviewed-by: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 49a0c17..c0226ab 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -830,8 +830,13 @@ ENTRY(native_iret) .global native_irq_return_iret native_irq_return_iret: + /* + * This may fault. Non-paranoid faults on return to userspace are + * handled by fixup_bad_iret. These include #SS, #GP, and #NP. + * Double-faults due to espfix64 are handled in do_double_fault. + * Other faults here are fatal. + */ iretq - _ASM_EXTABLE(native_irq_return_iret, bad_iret) #ifdef CONFIG_X86_ESPFIX64 native_irq_return_ldt: @@ -859,25 +864,6 @@ native_irq_return_ldt: jmp native_irq_return_iret #endif - .section .fixup,"ax" -bad_iret: - /* - * The iret traps when the %cs or %ss being restored is bogus. - * We've lost the original trap vector and error code. - * #GPF is the most likely one to get for an invalid selector. - * So pretend we completed the iret and took the #GPF in user mode. - * - * We are now running with the kernel GS after exception recovery. - * But error_entry expects us to have user GS to match the user %cs, - * so swap back. - */ - pushq $0 - - SWAPGS - jmp general_protection - - .previous - /* edi: workmask, edx: work */ retint_careful: CFI_RESTORE_STATE @@ -1369,17 +1355,16 @@ error_sti: /* * There are two places in the kernel that can potentially fault with - * usergs. Handle them here. The exception handlers after iret run with - * kernel gs again, so don't set the user space flag. B stepping K8s - * sometimes report an truncated RIP for IRET exceptions returning to - * compat mode. Check for these here too. + * usergs. Handle them here. B stepping K8s sometimes report a + * truncated RIP for IRET exceptions returning to compat mode. Check + * for these here too. */ error_kernelspace: CFI_REL_OFFSET rcx, RCX+8 incl %ebx leaq native_irq_return_iret(%rip),%rcx cmpq %rcx,RIP+8(%rsp) - je error_swapgs + je error_bad_iret movl %ecx,%eax /* zero extend */ cmpq %rax,RIP+8(%rsp) je bstep_iret @@ -1390,7 +1375,15 @@ error_kernelspace: bstep_iret: /* Fix truncated RIP */ movq %rcx,RIP+8(%rsp) - jmp error_swapgs + /* fall through */ + +error_bad_iret: + SWAPGS + mov %rsp,%rdi + call fixup_bad_iret + mov %rax,%rsp + decl %ebx /* Return to usergs */ + jmp error_sti CFI_ENDPROC END(error_entry) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 48035e9..de801f2 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -407,6 +407,35 @@ asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs) return regs; } NOKPROBE_SYMBOL(sync_regs); + +struct bad_iret_stack { + void *error_entry_ret; + struct pt_regs regs; +}; + +asmlinkage __visible +struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s) +{ + /* + * This is called from entry_64.S early in handling a fault + * caused by a bad iret to user mode. To handle the fault + * correctly, we want move our stack frame to task_pt_regs + * and we want to pretend that the exception came from the + * iret target. + */ + struct bad_iret_stack *new_stack = + container_of(task_pt_regs(current), + struct bad_iret_stack, regs); + + /* Copy the IRET target to the new stack. */ + memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8); + + /* Copy the remainder of the stack from the current stack. */ + memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip)); + + BUG_ON(!user_mode_vm(&new_stack->regs)); + return new_stack; +} #endif /* -- cgit v0.10.2 From 90e362f4a75d0911ca75e5cd95591a6cf1f169dc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Nov 2014 23:04:52 +0100 Subject: sched: Provide update_curr callbacks for stop/idle scheduling classes Chris bisected a NULL pointer deference in task_sched_runtime() to commit 6e998916dfe3 'sched/cputime: Fix clock_nanosleep()/clock_gettime() inconsistency'. Chris observed crashes in atop or other /proc walking programs when he started fork bombs on his machine. He assumed that this is a new exit race, but that does not make any sense when looking at that commit. What's interesting is that, the commit provides update_curr callbacks for all scheduling classes except stop_task and idle_task. While nothing can ever hit that via the clock_nanosleep() and clock_gettime() interfaces, which have been the target of the commit in question, the author obviously forgot that there are other code paths which invoke task_sched_runtime() do_task_stat(() thread_group_cputime_adjusted() thread_group_cputime() task_cputime() task_sched_runtime() if (task_current(rq, p) && task_on_rq_queued(p)) { update_rq_clock(rq); up->sched_class->update_curr(rq); } If the stats are read for a stomp machine task, aka 'migration/N' and that task is current on its cpu, this will happily call the NULL pointer of stop_task->update_curr. Ooops. Chris observation that this happens faster when he runs the fork bomb makes sense as the fork bomb will kick migration threads more often so the probability to hit the issue will increase. Add the missing update_curr callbacks to the scheduler classes stop_task and idle_task. While idle tasks cannot be monitored via /proc we have other means to hit the idle case. Fixes: 6e998916dfe3 'sched/cputime: Fix clock_nanosleep()/clock_gettime() inconsistency' Reported-by: Chris Mason Reported-and-tested-by: Borislav Petkov Signed-off-by: Thomas Gleixner Cc: Ingo Molnar Cc: Stanislaw Gruszka Cc: Peter Zijlstra Signed-off-by: Linus Torvalds diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index 67ad4e7..c65dac8 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -75,6 +75,10 @@ static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task return 0; } +static void update_curr_idle(struct rq *rq) +{ +} + /* * Simple, special scheduling class for the per-CPU idle tasks: */ @@ -101,4 +105,5 @@ const struct sched_class idle_sched_class = { .prio_changed = prio_changed_idle, .switched_to = switched_to_idle, + .update_curr = update_curr_idle, }; diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index 67426e5..79ffec4 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -102,6 +102,10 @@ get_rr_interval_stop(struct rq *rq, struct task_struct *task) return 0; } +static void update_curr_stop(struct rq *rq) +{ +} + /* * Simple, special scheduling class for the per-CPU stop tasks: */ @@ -128,4 +132,5 @@ const struct sched_class stop_sched_class = { .prio_changed = prio_changed_stop, .switched_to = switched_to_stop, + .update_curr = update_curr_stop, }; -- cgit v0.10.2 From 82975bc6a6df743b9a01810fb32cb65d0ec5d60b Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Fri, 21 Nov 2014 13:26:07 -0800 Subject: uprobes, x86: Fix _TIF_UPROBE vs _TIF_NOTIFY_RESUME x86 call do_notify_resume on paranoid returns if TIF_UPROBE is set but not on non-paranoid returns. I suspect that this is a mistake and that the code only works because int3 is paranoid. Setting _TIF_NOTIFY_RESUME in the uprobe code was probably a workaround for the x86 bug. With that bug fixed, we can remove _TIF_NOTIFY_RESUME from the uprobes code. Reported-by: Oleg Nesterov Acked-by: Srikar Dronamraju Acked-by: Borislav Petkov Signed-off-by: Andy Lutomirski Signed-off-by: Linus Torvalds diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 8540538..547e344 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -141,7 +141,7 @@ struct thread_info { /* Only used for 64 bit */ #define _TIF_DO_NOTIFY_MASK \ (_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME | \ - _TIF_USER_RETURN_NOTIFY) + _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE) /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 1d0af8a..ed8f2cd 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1640,7 +1640,6 @@ bool uprobe_deny_signal(void) if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) { utask->state = UTASK_SSTEP_TRAPPED; set_tsk_thread_flag(t, TIF_UPROBE); - set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); } } -- cgit v0.10.2 From 5d01410fe4d92081f349b013a2e7a95429e4f2c9 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 23 Nov 2014 15:25:20 -0800 Subject: Linux 3.18-rc6 diff --git a/Makefile b/Makefile index 00d618b..2fd5c4e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 18 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc6 NAME = Diseased Newt # *DOCUMENTATION* -- cgit v0.10.2 From 20ea60ca9952bd19d4b0d74719daba305aef5178 Mon Sep 17 00:00:00 2001 From: lucien Date: Sun, 23 Nov 2014 15:04:11 +0800 Subject: ip_tunnel: the lack of vti_link_ops' dellink() cause kernel panic Now the vti_link_ops do not point the .dellink, for fb tunnel device (ip_vti0), the net_device will be removed as the default .dellink is unregister_netdevice_queue,but the tunnel still in the tunnel list, then if we add a new vti tunnel, in ip_tunnel_find(): hlist_for_each_entry_rcu(t, head, hash_node) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && link == t->parms.link && ==> type == t->dev->type && ip_tunnel_key_match(&t->parms, flags, key)) break; } the panic will happen, cause dev of ip_tunnel *t is null: [ 3835.072977] IP: [] ip_tunnel_find+0x9d/0xc0 [ip_tunnel] [ 3835.073008] PGD b2c21067 PUD b7277067 PMD 0 [ 3835.073008] Oops: 0000 [#1] SMP ..... [ 3835.073008] Stack: [ 3835.073008] ffff8800b72d77f0 ffffffffa0411924 ffff8800bb956000 ffff8800b72d78e0 [ 3835.073008] ffff8800b72d78a0 0000000000000000 ffffffffa040d100 ffff8800b72d7858 [ 3835.073008] ffffffffa040b2e3 0000000000000000 0000000000000000 0000000000000000 [ 3835.073008] Call Trace: [ 3835.073008] [] ip_tunnel_newlink+0x64/0x160 [ip_tunnel] [ 3835.073008] [] vti_newlink+0x43/0x70 [ip_vti] [ 3835.073008] [] rtnl_newlink+0x4fa/0x5f0 [ 3835.073008] [] ? nla_strlcpy+0x5b/0x70 [ 3835.073008] [] ? rtnl_link_ops_get+0x40/0x60 [ 3835.073008] [] ? rtnl_newlink+0x13f/0x5f0 [ 3835.073008] [] rtnetlink_rcv_msg+0xa4/0x270 [ 3835.073008] [] ? sock_has_perm+0x75/0x90 [ 3835.073008] [] ? rtnetlink_rcv+0x30/0x30 [ 3835.073008] [] netlink_rcv_skb+0xa9/0xc0 [ 3835.073008] [] rtnetlink_rcv+0x28/0x30 .... modprobe ip_vti ip link del ip_vti0 type vti ip link add ip_vti0 type vti rmmod ip_vti do that one or more times, kernel will panic. fix it by assigning ip_tunnel_dellink to vti_link_ops' dellink, in which we skip the unregister of fb tunnel device. do the same on ip6_vti. Signed-off-by: Xin Long Signed-off-by: Cong Wang Signed-off-by: David S. Miller diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 3e86101..1a7e979 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -528,6 +528,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = { .validate = vti_tunnel_validate, .newlink = vti_newlink, .changelink = vti_changelink, + .dellink = ip_tunnel_dellink, .get_size = vti_get_size, .fill_info = vti_fill_info, }; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 31089d1..bcda14d 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -905,6 +905,15 @@ static int vti6_newlink(struct net *src_net, struct net_device *dev, return vti6_tnl_create2(dev); } +static void vti6_dellink(struct net_device *dev, struct list_head *head) +{ + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + if (dev != ip6n->fb_tnl_dev) + unregister_netdevice_queue(dev, head); +} + static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -980,6 +989,7 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = { .setup = vti6_dev_setup, .validate = vti6_validate, .newlink = vti6_newlink, + .dellink = vti6_dellink, .changelink = vti6_changelink, .get_size = vti6_get_size, .fill_info = vti6_fill_info, @@ -1020,6 +1030,7 @@ static int __net_init vti6_init_net(struct net *net) if (!ip6n->fb_tnl_dev) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) -- cgit v0.10.2 From 413cbf469a19e7662ba5025695bf5a573927105a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 1 Oct 2014 10:30:53 +0200 Subject: ALSA: hda - Limit 40bit DMA for AMD HDMI controllers AMD/ATI HDMI controller chip models, we already have a filter to lower to 32bit DMA, but the rest are supposed to be working with 64bit although the hardware doesn't really work with 63bit but only with 40 or 48bit DMA. In this patch, we take 40bit DMA for safety for the AMD/ATI controllers as the graphics drivers does. Signed-off-by: Takashi Iwai Signed-off-by: Benjamin Herrenschmidt CC: diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 16660f3..bd298ba 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1486,6 +1486,7 @@ static int azx_first_init(struct azx *chip) struct snd_card *card = chip->card; int err; unsigned short gcap; + unsigned int dma_bits = 64; #if BITS_PER_LONG != 64 /* Fix up base address on ULI M5461 */ @@ -1522,9 +1523,14 @@ static int azx_first_init(struct azx *chip) gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); + /* AMD devices support 40 or 48bit DMA, take the safe one */ + if (chip->pci->vendor == PCI_VENDOR_ID_AMD) + dma_bits = 40; + /* disable SB600 64bit support for safety */ if (chip->pci->vendor == PCI_VENDOR_ID_ATI) { struct pci_dev *p_smbus; + dma_bits = 40; p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); @@ -1554,9 +1560,11 @@ static int azx_first_init(struct azx *chip) } /* allow 64bit DMA address if supported by H/W */ - if ((gcap & AZX_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); - else { + if (!(gcap & AZX_GCAP_64OK)) + dma_bits = 32; + if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) { + pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits)); + } else { pci_set_dma_mask(pci, DMA_BIT_MASK(32)); pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); } -- cgit v0.10.2 From f144d1496b47e7450f41b767d0d91c724c2198bc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 3 Oct 2014 15:13:24 +1000 Subject: PCI/MSI: Add device flag indicating that 64-bit MSIs don't work This can be set by quirks/drivers to be used by the architecture code that assigns the MSI addresses. We additionally add verification in the core MSI code that the values assigned by the architecture do satisfy the limitation in order to fail gracefully if they don't (ie. the arch hasn't been updated to deal with that quirk yet). Signed-off-by: Benjamin Herrenschmidt CC: Acked-by: Bjorn Helgaas diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9fab30a..084587d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -590,6 +590,20 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev) return entry; } +static int msi_verify_entries(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + if (!dev->no_64bit_msi || !entry->msg.address_hi) + continue; + dev_err(&dev->dev, "Device has broken 64-bit MSI but arch" + " tried to assign one above 4G\n"); + return -EIO; + } + return 0; +} + /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function @@ -627,6 +641,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) return ret; } + ret = msi_verify_entries(dev); + if (ret) { + msi_mask_irq(entry, mask, ~mask); + free_msi_irqs(dev); + return ret; + } + ret = populate_msi_sysfs(dev); if (ret) { msi_mask_irq(entry, mask, ~mask); @@ -739,6 +760,11 @@ static int msix_capability_init(struct pci_dev *dev, if (ret) goto out_avail; + /* Check if all MSI entries honor device restrictions */ + ret = msi_verify_entries(dev); + if (ret) + goto out_free; + /* * Some devices require MSI-X to be enabled before we can touch the * MSI-X registers. We need to mask all the vectors to prevent diff --git a/include/linux/pci.h b/include/linux/pci.h index 5be8db4..4c8ac5f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -331,6 +331,7 @@ struct pci_dev { unsigned int is_added:1; unsigned int is_busmaster:1; /* device is busmaster */ unsigned int no_msi:1; /* device may not use msi */ + unsigned int no_64bit_msi:1; /* device may only use 32-bit MSIs */ unsigned int block_cfg_access:1; /* config space access is blocked */ unsigned int broken_parity_status:1; /* Device generates false positive parity */ unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */ -- cgit v0.10.2 From 91ed6fd2c383bb8f02d66e98b4a4d2f7207249dc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 3 Oct 2014 15:18:59 +1000 Subject: gpu/radeon: Set flag to indicate broken 64-bit MSI Some radeon ASICs don't support all 64 address bits of MSIs despite advertising support for 64-bit MSIs in their configuration space. This breaks on systems such as IBM POWER7/8, where 64-bit MSIs can be assigned with some of the high address bits set. This makes use of the newly introduced "no_64bit_msi" flag in structure pci_dev to allow the MSI allocation code to fallback to 32-bit MSIs on those adapters. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Alex Deucher CC: --- Adding Alex's review tag. Patch to the driver is identical to the reviewed one, I dropped the arch/powerpc hunk rewrote the subject and cset comment. diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 7784911..00fc597 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -185,6 +185,16 @@ static bool radeon_msi_ok(struct radeon_device *rdev) if (rdev->flags & RADEON_IS_AGP) return false; + /* + * Older chips have a HW limitation, they can only generate 40 bits + * of address for "64-bit" MSIs which breaks on some platforms, notably + * IBM POWER servers, so we limit them + */ + if (rdev->family < CHIP_BONAIRE) { + dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n"); + rdev->pdev->no_64bit_msi = 1; + } + /* force MSI on */ if (radeon_msi == 1) return true; -- cgit v0.10.2 From db79afa1e57925ba96ab18514c0ebe42a28e393e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 24 Nov 2014 14:17:08 +1100 Subject: sound/radeon: Move 64-bit MSI quirk from arch to driver A number of radeon cards have a HW limitation causing them to be unable to generate the full 64-bit of address bits for MSIs. This breaks MSIs on some platforms such as POWER machines. We used to have a powerpc specific quirk to address that on a single card, but this doesn't scale very well, this is better put under control of the drivers who know precisely what a given HW revision can do. We now have a generic quirk in the PCI code. We should set it appropriately for all radeon's from the audio driver. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Takashi Iwai Reviewed-by: Alex Deucher CC: diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bd298ba..48b6c5a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -298,7 +298,8 @@ enum { /* quirks for ATI/AMD HDMI */ #define AZX_DCAPS_PRESET_ATI_HDMI \ - (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) + (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\ + AZX_DCAPS_NO_MSI64) /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ @@ -1510,9 +1511,14 @@ static int azx_first_init(struct azx *chip) return -ENXIO; } - if (chip->msi) + if (chip->msi) { + if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { + dev_dbg(card->dev, "Disabling 64bit MSI\n"); + pci->no_64bit_msi = true; + } if (pci_enable_msi(pci) < 0) chip->msi = 0; + } if (azx_acquire_irq(chip, 0) < 0) return -EBUSY; diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index 949cd43..5016014 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -171,6 +171,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ +#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ /* HD Audio class code */ #define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 -- cgit v0.10.2 From 360743814c4082515581aa23ab1d8e699e1fbe88 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 7 Oct 2014 16:12:36 +1100 Subject: powerpc/powernv: Honor the generic "no_64bit_msi" flag Instead of the arch specific quirk which we are deprecating and that drivers don't understand. Signed-off-by: Benjamin Herrenschmidt CC: diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 468a0f2..7a8e806 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1509,7 +1509,6 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, unsigned int is_64, struct msi_msg *msg) { struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev); - struct pci_dn *pdn = pci_get_pdn(dev); unsigned int xive_num = hwirq - phb->msi_base; __be32 data; int rc; @@ -1523,7 +1522,7 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, return -ENXIO; /* Force 32-bit MSI on some broken devices */ - if (pdn && pdn->force_32bit_msi) + if (dev->no_64bit_msi) is_64 = 0; /* Assign XIVE to PE */ diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index b2187d0..4b20f2c 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -50,7 +50,6 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) { struct pci_controller *hose = pci_bus_to_host(pdev->bus); struct pnv_phb *phb = hose->private_data; - struct pci_dn *pdn = pci_get_pdn(pdev); struct msi_desc *entry; struct msi_msg msg; int hwirq; @@ -60,7 +59,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) if (WARN_ON(!phb) || !phb->msi_bmp.bitmap) return -ENODEV; - if (pdn && pdn->force_32bit_msi && !phb->msi32_support) + if (pdev->no_64bit_msi && !phb->msi32_support) return -ENODEV; list_for_each_entry(entry, &pdev->msi_list, list) { -- cgit v0.10.2 From 415072a041bf50dbd6d56934ffc0cbbe14c97be8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 7 Oct 2014 16:12:55 +1100 Subject: powerpc/pseries: Honor the generic "no_64bit_msi" flag Instead of the arch specific quirk which we are deprecating Signed-off-by: Benjamin Herrenschmidt CC: diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 8ab5add..8b909e9 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -420,7 +420,7 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type) */ again: if (type == PCI_CAP_ID_MSI) { - if (pdn->force_32bit_msi) { + if (pdev->no_64bit_msi) { rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec); if (rc < 0) { /* -- cgit v0.10.2 From 31345e1a071e4e5f2fa8b6be5ca7d1cbce20cfca Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 7 Oct 2014 16:13:34 +1100 Subject: powerpc/pci: Remove unused force_32bit_msi quirk This is now fully replaced with the generic "no_64bit_msi" one that is set by the respective drivers directly. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 4ca90a3..725247b 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -159,8 +159,6 @@ struct pci_dn { int pci_ext_config_space; /* for pci devices */ - bool force_32bit_msi; - struct pci_dev *pcidev; /* back-pointer to the pci device */ #ifdef CONFIG_EEH struct eeh_dev *edev; /* eeh device */ diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 155013d..b15194e 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -266,13 +266,3 @@ int pcibus_to_node(struct pci_bus *bus) } EXPORT_SYMBOL(pcibus_to_node); #endif - -static void quirk_radeon_32bit_msi(struct pci_dev *dev) -{ - struct pci_dn *pdn = pci_get_pdn(dev); - - if (pdn) - pdn->force_32bit_msi = true; -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon_32bit_msi); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon_32bit_msi); -- cgit v0.10.2 From b61a393945298c11093a1fee3c89cffa64ff5d0e Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 12 Nov 2014 13:47:14 +0800 Subject: MIPS: Fix a copy & paste error in unistd.h Commit 5df4c8dbbc (MIPS: Wire up bpf syscall.) break the N32 build because of a copy & paste error. Signed-off-by: Huacai Chen Cc: John Crispin Cc: Steven J. Hill Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang Cc: Zhangjin Wu Patchwork: https://patchwork.linux-mips.org/patch/8390/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h index 9dc5856..d001bb1 100644 --- a/arch/mips/include/uapi/asm/unistd.h +++ b/arch/mips/include/uapi/asm/unistd.h @@ -1045,7 +1045,7 @@ #define __NR_seccomp (__NR_Linux + 316) #define __NR_getrandom (__NR_Linux + 317) #define __NR_memfd_create (__NR_Linux + 318) -#define __NR_memfd_create (__NR_Linux + 319) +#define __NR_bpf (__NR_Linux + 319) /* * Offset of the last N32 flavoured syscall -- cgit v0.10.2 From a54b8b0800f6fed5044bc6750ee757975a0ec35e Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 24 Oct 2014 00:41:45 +0200 Subject: MIPS: SEAD3: Fix LED device registration. This isn't a module and shouldn't be one. Signed-off-by: Ralf Baechle Cc: Markos Chandras Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8202/ diff --git a/arch/mips/mti-sead3/sead3-leds.c b/arch/mips/mti-sead3/sead3-leds.c index 20102a6..c427c57 100644 --- a/arch/mips/mti-sead3/sead3-leds.c +++ b/arch/mips/mti-sead3/sead3-leds.c @@ -5,7 +5,7 @@ * * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. */ -#include +#include #include #include @@ -76,8 +76,4 @@ static int __init led_init(void) return platform_device_register(&fled_device); } -module_init(led_init); - -MODULE_AUTHOR("Chris Dearman "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("LED probe driver for SEAD-3"); +device_initcall(led_init); -- cgit v0.10.2 From 3dc4bf310a5c74eefb23af026034cce2a584296e Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 7 Oct 2014 12:57:45 +0900 Subject: MIPS: rtlx: Remove KERN_DEBUG from pr_debug() arguments in rtlx.c Signed-off-by: Masanari Iida Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/7938/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index 31b1b76..c5c4fd5 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -94,12 +94,12 @@ int rtlx_open(int index, int can_sleep) int ret = 0; if (index >= RTLX_CHANNELS) { - pr_debug(KERN_DEBUG "rtlx_open index out of range\n"); + pr_debug("rtlx_open index out of range\n"); return -ENOSYS; } if (atomic_inc_return(&channel_wqs[index].in_open) > 1) { - pr_debug(KERN_DEBUG "rtlx_open channel %d already opened\n", index); + pr_debug("rtlx_open channel %d already opened\n", index); ret = -EBUSY; goto out_fail; } -- cgit v0.10.2 From 7f0dd7683c4d7e01dc99d8922190af6bcc9a0860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 3 Sep 2014 07:36:51 +0200 Subject: MIPS: Fix info about plat_setup in arch_mem_init comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/7607/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index d21ec57..083c3c2 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -485,7 +485,7 @@ static void __init bootmem_init(void) * NOTE: historically plat_mem_setup did the entire platform initialization. * This was rather impractical because it meant plat_mem_setup had to * get away without any kind of memory allocator. To keep old code from - * breaking plat_setup was just renamed to plat_setup and a second platform + * breaking plat_setup was just renamed to plat_mem_setup and a second platform * initialization hook for anything else was introduced. */ -- cgit v0.10.2 From 83fd43449baaf88fe5c03dd0081a062041837c51 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Wed, 5 Nov 2014 08:25:37 +0000 Subject: MIPS: r4kcache: Add EVA case for protected_writeback_dcache_line Commit de8974e3f76c0 ("MIPS: asm: r4kcache: Add EVA cache flushing functions") added cache function for EVA using the cachee instruction. However, it didn't add a case for the protected_writeback_dcache_line. mips_dsemul() calls r4k_flush_cache_sigtramp() which in turn uses the protected_writeback_dcache_line() to flush the trampoline code back to memory. This used the wrong "cache" instruction leading to random userland crashes on non-FPU cores. Signed-off-by: Markos Chandras Cc: # v3.15+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8331/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/r4kcache.h b/arch/mips/include/asm/r4kcache.h index 4520adc..cd6e0af 100644 --- a/arch/mips/include/asm/r4kcache.h +++ b/arch/mips/include/asm/r4kcache.h @@ -257,7 +257,11 @@ static inline void protected_flush_icache_line(unsigned long addr) */ static inline void protected_writeback_dcache_line(unsigned long addr) { +#ifdef CONFIG_EVA + protected_cachee_op(Hit_Writeback_Inv_D, addr); +#else protected_cache_op(Hit_Writeback_Inv_D, addr); +#endif } static inline void protected_writeback_scache_line(unsigned long addr) -- cgit v0.10.2 From 14aa136161f8096b4de3f90b5b81de2586107947 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 4 Nov 2014 02:23:45 +0100 Subject: MIPS: uaccess.h: Fix strnlen_user comment. Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index 133678a..22a5624 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -1422,7 +1422,7 @@ static inline long __strnlen_user(const char __user *s, long n) } /* - * strlen_user: - Get the size of a string in user space. + * strnlen_user: - Get the size of a string in user space. * @str: The string to measure. * * Context: User context only. This function may sleep. @@ -1431,9 +1431,7 @@ static inline long __strnlen_user(const char __user *s, long n) * * Returns the size of the string INCLUDING the terminating NUL. * On exception, returns 0. - * - * If there is a limit on the length of a valid string, you may wish to - * consider using strnlen_user() instead. + * If the string is too long, returns a value greater than @n. */ static inline long strnlen_user(const char __user *s, long n) { -- cgit v0.10.2 From 4ec8f9e9b08451303253249e4e302f10ee23d565 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Mon, 20 Oct 2014 21:27:51 -0700 Subject: MIPS: BMIPS: Fix ".previous without corresponding .section" warnings Commit 078a55fc824c1 ("Delete __cpuinit/__CPUINIT usage from MIPS code") removed our __CPUINIT directives, so now the ".previous" directives are superfluous. Remove them. Signed-off-by: Kevin Cernekee Cc: f.fainelli@gmail.com Cc: mbizon@freebox.fr Cc: jogo@openwrt.org Cc: jfraser@broadcom.com Cc: linux-mips@linux-mips.org Cc: devicetree@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/8156/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/bmips_vec.S b/arch/mips/kernel/bmips_vec.S index 290c23b..8649507 100644 --- a/arch/mips/kernel/bmips_vec.S +++ b/arch/mips/kernel/bmips_vec.S @@ -208,7 +208,6 @@ bmips_reset_nmi_vec_end: END(bmips_reset_nmi_vec) .set pop - .previous /*********************************************************************** * CPU1 warm restart vector (used for second and subsequent boots). @@ -281,5 +280,3 @@ LEAF(bmips_enable_xks01) jr ra END(bmips_enable_xks01) - - .previous -- cgit v0.10.2 From cf0a8aa0226da5de88011e7f30eff22a894b2f49 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 10 Nov 2014 12:25:34 +0000 Subject: MIPS: cpu-probe: Set the FTLB probability bit on supported cores Make use of the Config6/FLTBP bit to set the probability of a TLBWR instruction to hit the FTLB or the VTLB. A value of 0 (which may be the default value on certain cores, such as proAptiv or P5600) means that a TLBWR instruction will never hit the VTLB which leads to performance limitations since it effectively decreases the number of available TLB slots. Signed-off-by: Markos Chandras Reviewed-by: James Hogan Cc: # v3.15+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8368/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index b46cd22..22a135a 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -661,6 +661,8 @@ #define MIPS_CONF6_SYND (_ULCAST_(1) << 13) /* proAptiv FTLB on/off bit */ #define MIPS_CONF6_FTLBEN (_ULCAST_(1) << 15) +/* FTLB probability bits */ +#define MIPS_CONF6_FTLBP_SHIFT (16) #define MIPS_CONF7_WII (_ULCAST_(1) << 31) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index d5a4f38..dc49cf3 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -193,6 +193,32 @@ static void set_isa(struct cpuinfo_mips *c, unsigned int isa) static char unknown_isa[] = KERN_ERR \ "Unsupported ISA type, c0.config0: %d."; +static unsigned int calculate_ftlb_probability(struct cpuinfo_mips *c) +{ + + unsigned int probability = c->tlbsize / c->tlbsizevtlb; + + /* + * 0 = All TLBWR instructions go to FTLB + * 1 = 15:1: For every 16 TBLWR instructions, 15 go to the + * FTLB and 1 goes to the VTLB. + * 2 = 7:1: As above with 7:1 ratio. + * 3 = 3:1: As above with 3:1 ratio. + * + * Use the linear midpoint as the probability threshold. + */ + if (probability >= 12) + return 1; + else if (probability >= 6) + return 2; + else + /* + * So FTLB is less than 4 times bigger than VTLB. + * A 3:1 ratio can still be useful though. + */ + return 3; +} + static void set_ftlb_enable(struct cpuinfo_mips *c, int enable) { unsigned int config6; @@ -203,9 +229,14 @@ static void set_ftlb_enable(struct cpuinfo_mips *c, int enable) case CPU_P5600: /* proAptiv & related cores use Config6 to enable the FTLB */ config6 = read_c0_config6(); + /* Clear the old probability value */ + config6 &= ~(3 << MIPS_CONF6_FTLBP_SHIFT); if (enable) /* Enable FTLB */ - write_c0_config6(config6 | MIPS_CONF6_FTLBEN); + write_c0_config6(config6 | + (calculate_ftlb_probability(c) + << MIPS_CONF6_FTLBP_SHIFT) + | MIPS_CONF6_FTLBEN); else /* Disable FTLB */ write_c0_config6(config6 & ~MIPS_CONF6_FTLBEN); -- cgit v0.10.2 From 14fa12df1d6bc1d3389a0fa842e0ebd8e8a9af26 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 28 Oct 2014 11:25:51 +0000 Subject: MIPS: fix EVA & non-SMP non-FPU FP context signal handling The save_fp_context & restore_fp_context pointers were being assigned to the wrong variables if either: - The kernel is configured for UP & runs on a system without an FPU, since b2ead5282885 "MIPS: Move & rename fpu_emulator_{save,restore}_context". - The kernel is configured for EVA, since ca750649e08c "MIPS: kernel: signal: Prevent save/restore FPU context in user memory". This would lead to FP context being clobbered incorrectly when setting up a sigcontext, then the garbage values being saved uselessly when returning from the signal. Fix by swapping the pointer assignments appropriately. Signed-off-by: Paul Burton Cc: stable@vger.kernel.org # v3.15+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8230/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 1d57605..16f1e4f 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -658,13 +658,13 @@ static int signal_setup(void) save_fp_context = _save_fp_context; restore_fp_context = _restore_fp_context; } else { - save_fp_context = copy_fp_from_sigcontext; - restore_fp_context = copy_fp_to_sigcontext; + save_fp_context = copy_fp_to_sigcontext; + restore_fp_context = copy_fp_from_sigcontext; } #endif /* CONFIG_SMP */ #else - save_fp_context = copy_fp_from_sigcontext;; - restore_fp_context = copy_fp_to_sigcontext; + save_fp_context = copy_fp_to_sigcontext; + restore_fp_context = copy_fp_from_sigcontext; #endif return 0; -- cgit v0.10.2 From 26927f76499849e095714452b8a4e09350f6a3b9 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Thu, 20 Nov 2014 01:05:38 +0200 Subject: MIPS: Loongson: Make platform serial setup always built-in. If SERIAL_8250 is compiled as a module, the platform specific setup for Loongson will be a module too, and it will not work very well. At least on Loongson 3 it will trigger a build failure, since loongson_sysconf is not exported to modules. Fix by making the platform specific serial code always built-in. Signed-off-by: Aaro Koskinen Reported-by: Ralf Baechle Cc: stable@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: Huacai Chen Cc: Markos Chandras Patchwork: https://patchwork.linux-mips.org/patch/8533/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile index 0bb9cc9..d87e033 100644 --- a/arch/mips/loongson/common/Makefile +++ b/arch/mips/loongson/common/Makefile @@ -11,7 +11,8 @@ obj-$(CONFIG_PCI) += pci.o # Serial port support # obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_SERIAL_8250) += serial.o +loongson-serial-$(CONFIG_SERIAL_8250) := serial.o +obj-y += $(loongson-serial-m) $(loongson-serial-y) obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o obj-$(CONFIG_LOONGSON_MC146818) += rtc.o -- cgit v0.10.2 From 717ce646395b8606515563edc5f57b27cf559bee Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 24 Sep 2014 10:55:10 -0700 Subject: MIPS: Netlogic: handle modular USB case Commit 1004165f346a ("MIPS: Netlogic: USB support for XLP") and then commit 9eac3591e78b ("MIPS: Netlogic: Add support for USB on XLP2xx") added usb-init and usb-init-xlp2 as objects to build when CONFIG_USB is enabled. If CONFIG_USB is made modular, these two files will also get built as modules (obj-m), which will result in the following linking failure: ERROR: "nlm_io_base" [arch/mips/netlogic/xlp/usb-init.ko] undefined! ERROR: "nlm_nodes" [arch/mips/netlogic/xlp/usb-init-xlp2.ko] undefined! ERROR: "nlm_set_pic_extra_ack" [arch/mips/netlogic/xlp/usb-init-xlp2.ko] undefined! ERROR: "xlp_socdev_to_node" [arch/mips/netlogic/xlp/usb-init-xlp2.ko] undefined! ERROR: "nlm_io_base" [arch/mips/netlogic/xlp/usb-init-xlp2.ko] undefined! Just check whether CONFIG_USB is defined for this build, and if that is the case, add these objects to the list of built-in object files. Signed-off-by: Florian Fainelli Cc: linux-mips@linux-mips.org Cc: ganesanr@broadcom.com Cc: jchandra@broadcom.com Patchwork: https://patchwork.linux-mips.org/patch/7854/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/netlogic/xlp/Makefile b/arch/mips/netlogic/xlp/Makefile index be358a8..577889f 100644 --- a/arch/mips/netlogic/xlp/Makefile +++ b/arch/mips/netlogic/xlp/Makefile @@ -1,6 +1,8 @@ obj-y += setup.o nlm_hal.o cop2-ex.o dt.o obj-$(CONFIG_SMP) += wakeup.o -obj-$(CONFIG_USB) += usb-init.o -obj-$(CONFIG_USB) += usb-init-xlp2.o +ifdef CONFIG_USB +obj-y += usb-init.o +obj-y += usb-init-xlp2.o +endif obj-$(CONFIG_SATA_AHCI) += ahci-init.o obj-$(CONFIG_SATA_AHCI) += ahci-init-xlp2.o -- cgit v0.10.2 From 3964917c6b69f2e5908f603d8408390d40aadaf5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 24 Sep 2014 10:55:11 -0700 Subject: MIPS: Netlogic: handle modular AHCI builds Commits a951440971d0 ("MIPS: Netlogic: Support for XLP3XX on-chip SATA") and fedfcb1137d2 ("MIPS: Netlogic: XLP9XX on-chip SATA support") added ahci-init and ahci-init-xlp2 as objects to build when CONFIG_SATA_AHCI is enabled. If CONFIG_SATA_AHCI is made modular, these two files will also get built as modules (obj-m), which will result in the following linking failure: ERROR: "nlm_set_pic_extra_ack" [arch/mips/netlogic/xlp/ahci-init.ko] undefined! ERROR: "nlm_io_base" [arch/mips/netlogic/xlp/ahci-init.ko] undefined! ERROR: "nlm_nodes" [arch/mips/netlogic/xlp/ahci-init-xlp2.ko] undefined! ERROR: "nlm_set_pic_extra_ack" [arch/mips/netlogic/xlp/ahci-init-xlp2.ko] undefined! ERROR: "xlp_socdev_to_node" [arch/mips/netlogic/xlp/ahci-init-xlp2.ko] undefined! ERROR: "nlm_io_base" [arch/mips/netlogic/xlp/ahci-init-xlp2.ko] undefined! Just check whether CONFIG_SATA_AHCI is defined for this build, and if that is the case, add these objects to the list of built-in object files. Signed-off-by: Florian Fainelli Cc: linux-mips@linux-mips.org Cc: ganesanr@broadcom.com Cc: jchandra@broadcom.com Patchwork: https://patchwork.linux-mips.org/patch/7855/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/netlogic/xlp/Makefile b/arch/mips/netlogic/xlp/Makefile index 577889f..6b43af0 100644 --- a/arch/mips/netlogic/xlp/Makefile +++ b/arch/mips/netlogic/xlp/Makefile @@ -4,5 +4,7 @@ ifdef CONFIG_USB obj-y += usb-init.o obj-y += usb-init-xlp2.o endif -obj-$(CONFIG_SATA_AHCI) += ahci-init.o -obj-$(CONFIG_SATA_AHCI) += ahci-init-xlp2.o +ifdef CONFIG_SATA_AHCI +obj-y += ahci-init.o +obj-y += ahci-init-xlp2.o +endif -- cgit v0.10.2 From 76cff82943b9744ec4f0ebd84f8ed888a6e509b3 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Wed, 16 Jul 2014 08:53:39 +0100 Subject: MIPS: kernel: cps-vec: Set ISA level to mips32r2 for the MIPS MT ASE Fixes the following build warnings: arch/mips/kernel/cps-vec.S: Assembler messages: arch/mips/kernel/cps-vec.S:228: Warning: the `mt' extension requires MIPS32 revision 2 or greater [...] arch/mips/kernel/cps-vec.S: Assembler messages: arch/mips/kernel/cps-vec.S:345: Warning: the `mt' extension requires MIPS32 revision 2 or greater Signed-off-by: Markos Chandras Cc: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/7355/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S index e6e97d2..0384b05 100644 --- a/arch/mips/kernel/cps-vec.S +++ b/arch/mips/kernel/cps-vec.S @@ -229,6 +229,7 @@ LEAF(mips_cps_core_init) nop .set push + .set mips32r2 .set mt /* Only allow 1 TC per VPE to execute... */ @@ -345,6 +346,7 @@ LEAF(mips_cps_boot_vpes) nop .set push + .set mips32r2 .set mt 1: /* Enter VPE configuration state */ -- cgit v0.10.2 From 9e2b53725a209539b195ee359894bbbf8c16c885 Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 21 Jul 2014 08:46:14 +0100 Subject: MIPS: Kconfig: Don't allow both microMIPS and SmartMIPS to be selected. microMIPS and SmartMIPS can't be used together. This fixes the following build problem: Warning: the 32-bit microMIPS architecture does not support the `smartmips' extension arch/mips/kernel/entry.S:90: Error: unrecognized opcode `mtlhx $24' [...] arch/mips/kernel/entry.S:109: Error: unrecognized opcode `mtlhx $24' Signed-off-by: Markos Chandras Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/7421/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index f43aa53..9536ef9 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2101,9 +2101,17 @@ config 64BIT_PHYS_ADDR config ARCH_PHYS_ADDR_T_64BIT def_bool 64BIT_PHYS_ADDR +choice + prompt "SmartMIPS or microMIPS ASE support" + +config CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS + bool "None" + help + Select this if you want neither microMIPS nor SmartMIPS support + config CPU_HAS_SMARTMIPS depends on SYS_SUPPORTS_SMARTMIPS - bool "Support for the SmartMIPS ASE" + bool "SmartMIPS" help SmartMIPS is a extension of the MIPS32 architecture aimed at increased security at both hardware and software level for @@ -2115,11 +2123,13 @@ config CPU_HAS_SMARTMIPS config CPU_MICROMIPS depends on SYS_SUPPORTS_MICROMIPS - bool "Build kernel using microMIPS ISA" + bool "microMIPS" help When this option is enabled the kernel will be built using the microMIPS ISA +endchoice + config CPU_HAS_MSA bool "Support for the MIPS SIMD Architecture (EXPERIMENTAL)" depends on CPU_SUPPORTS_MSA -- cgit v0.10.2 From b4da18b3714a35c1123522810cc5ec0cd2ee4101 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Thu, 13 Nov 2014 09:52:00 -0600 Subject: MIPS: Fix address type used for early memory detection. In 'early_parse_mem' the data type used for the start and size of a memory region specified on the command line is incorrect. If 64-bit addressing is used, the value gets truncated. Signed-off-by: Steven J. Hill Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8456/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 083c3c2..f3b635f 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -493,7 +493,7 @@ static int usermem __initdata; static int __init early_parse_mem(char *p) { - unsigned long start, size; + phys_t start, size; /* * If a user specifies memory size, we -- cgit v0.10.2 From bdfa7542d40e6251c232a802231b37116bd31b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 27 May 2014 21:33:09 +0300 Subject: drm/i915: Ignore SURFLIVE and flip counter when the GPU gets reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During a GPU reset we need to get pending page flip cleared out since the ring contents are gone and flip will never complete on its own. This used to work until the mmio vs. CS flip race detection came about. That piece of code is looking for a specific surface address in the SURFLIVE register, but as a flip to that address may never happen the check may never pass. So we should just skip the SURFLIVE and flip counter checks when the GPU gets reset. intel_display_handle_reset() tries to effectively complete the flip anyway by calling .update_primary_plane(). But that may not satisfy the conditions of the mmio vs. CS race detection since there's no guarantee that a modeset didn't sneak in between the GPU reset and intel_display_handle_reset(). Such a modeset will not wait for pending flips due to the ongoing GPU reset, and then the primary plane updates performed by intel_display_handle_reset() will already use the new surface address, and thus the surface address the flip is waiting for might never appear in SURFLIVE. The result is that the flip will never complete and attempts to perform further page flips will fail with -EBUSY. During the GPU reset intel_crtc_has_pending_flip() will return false regardless, so the deadlock with a modeset vs. the error work acquiring crtc->mutex was avoided. And the reset_counter check in intel_crtc_has_pending_flip() actually made this bug even less severe since it allowed normal modesets to go through even though there's a pending flip. This is a regression introduced by me here: commit 75f7f3ec600524c9544cc31695155f1a9ddbe1d9 Author: Ville Syrjälä Date: Tue Apr 15 21:41:34 2014 +0300 drm/i915: Fix mmio vs. CS flip race on ILK+ Testcase: igt/kms_flip/flip-vs-panning-vs-hang Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Reviewed-by: Daniel Vetter Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f0a1a56..8bcdb98 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9408,6 +9408,10 @@ static bool page_flip_finished(struct intel_crtc *crtc) struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + if (i915_reset_in_progress(&dev_priv->gpu_error) || + crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + return true; + /* * The relevant registers doen't exist on pre-ctg. * As the flip done interrupt doesn't trigger for mmio -- cgit v0.10.2 From be6572fdb1bfbe23b2624d477de50af50b02f5d6 Mon Sep 17 00:00:00 2001 From: Yuri Chislov Date: Mon, 24 Nov 2014 11:25:15 +0100 Subject: ipv6: gre: fix wrong skb->protocol in WCCP When using GRE redirection in WCCP, it sets the wrong skb->protocol, that is, ETH_P_IP instead of ETH_P_IPV6 for the encapuslated traffic. Fixes: c12b395a4664 ("gre: Support GRE over IPv6") Cc: Dmitry Kozlov Signed-off-by: Yuri Chislov Tested-by: Yuri Chislov Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 4564e1f..0e32d2e 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -502,11 +502,11 @@ static int ip6gre_rcv(struct sk_buff *skb) skb->protocol = gre_proto; /* WCCP version 1 and 2 protocol decoding. - * - Change protocol to IP + * - Change protocol to IPv6 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header */ if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) { - skb->protocol = htons(ETH_P_IP); + skb->protocol = htons(ETH_P_IPV6); if ((*(h + offset) & 0xF0) != 0x40) offset += 4; } -- cgit v0.10.2 From 2dd34339ac6305c4f2f4e589b858212e339d31e9 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 24 Nov 2014 13:58:00 +0300 Subject: xen-netback: do not report success if backend_create_xenvif() fails If xenvif_alloc() or xenbus_scanf() fail in backend_create_xenvif(), xenbus is left in offline mode but netback_probe() reports success. The patch implements propagation of error code for backend_create_xenvif(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Wei Liu Signed-off-by: David S. Miller diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 4e56a27..fab0d4b 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -39,7 +39,7 @@ struct backend_info { static int connect_rings(struct backend_info *be, struct xenvif_queue *queue); static void connect(struct backend_info *be); static int read_xenbus_vif_flags(struct backend_info *be); -static void backend_create_xenvif(struct backend_info *be); +static int backend_create_xenvif(struct backend_info *be); static void unregister_hotplug_status_watch(struct backend_info *be); static void set_backend_state(struct backend_info *be, enum xenbus_state state); @@ -352,7 +352,9 @@ static int netback_probe(struct xenbus_device *dev, be->state = XenbusStateInitWait; /* This kicks hotplug scripts, so do it immediately. */ - backend_create_xenvif(be); + err = backend_create_xenvif(be); + if (err) + goto fail; return 0; @@ -397,19 +399,19 @@ static int netback_uevent(struct xenbus_device *xdev, } -static void backend_create_xenvif(struct backend_info *be) +static int backend_create_xenvif(struct backend_info *be) { int err; long handle; struct xenbus_device *dev = be->dev; if (be->vif != NULL) - return; + return 0; err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%li", &handle); if (err != 1) { xenbus_dev_fatal(dev, err, "reading handle"); - return; + return (err < 0) ? err : -EINVAL; } be->vif = xenvif_alloc(&dev->dev, dev->otherend_id, handle); @@ -417,10 +419,11 @@ static void backend_create_xenvif(struct backend_info *be) err = PTR_ERR(be->vif); be->vif = NULL; xenbus_dev_fatal(dev, err, "creating interface"); - return; + return err; } kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE); + return 0; } static void backend_disconnect(struct backend_info *be) -- cgit v0.10.2 From 6e58040b848162d77243cf499d026c6253278e29 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 24 Nov 2014 13:32:16 +0200 Subject: af_packet: fix sparse warning af_packet produces lots of these: net/packet/af_packet.c:384:39: warning: incorrect type in return expression (different modifiers) net/packet/af_packet.c:384:39: expected struct page [pure] * net/packet/af_packet.c:384:39: got struct page * this seems to be because sparse does not realize that _pure refers to function, not the returned pointer. Tweak code slightly to avoid the warning. Signed-off-by: Michael S. Tsirkin Signed-off-by: David S. Miller diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 87d20f4..07c04a8 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -378,7 +378,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync) __unregister_prot_hook(sk, sync); } -static inline __pure struct page *pgv_to_page(void *addr) +static inline struct page * __pure pgv_to_page(void *addr) { if (is_vmalloc_addr(addr)) return vmalloc_to_page(addr); -- cgit v0.10.2 From 91a0b603469069cdcce4d572b7525ffc9fd352a6 Mon Sep 17 00:00:00 2001 From: Jane Zhou Date: Mon, 24 Nov 2014 11:44:08 -0800 Subject: net/ping: handle protocol mismatching scenario ping_lookup() may return a wrong sock if sk_buff's and sock's protocols dont' match. For example, sk_buff's protocol is ETH_P_IPV6, but sock's sk_family is AF_INET, in that case, if sk->sk_bound_dev_if is zero, a wrong sock will be returned. the fix is to "continue" the searching, if no matching, return NULL. Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Cc: netdev@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Jane Zhou Signed-off-by: Yiwei Zhao Signed-off-by: David S. Miller diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 57f7c98..85a02a7 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -217,6 +217,8 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) &ipv6_hdr(skb)->daddr)) continue; #endif + } else { + continue; } if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) -- cgit v0.10.2 From 263e80b43559a6103e178a9176938ce171b23872 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 24 Nov 2014 11:22:38 +0100 Subject: usb-quirks: Add reset-resume quirk for MS Wireless Laser Mouse 6000 This wireless mouse receiver needs a reset-resume quirk to properly come out of reset. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1165206 Signed-off-by: Hans de Goede Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 39b4081..96fafed 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft Wireless Laser Mouse 6000 Receiver */ + { USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, -- cgit v0.10.2 From d0ab54783f2de0c216115333eca1a8d3d5c3e75b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 19 Nov 2014 23:33:07 -0800 Subject: Input: elantech - trust firmware about trackpoint presence Only try to parse data as coming from trackpoint if firmware told us that trackpoint is present. Fixes commit caeb0d37fa3e387eb0dd22e5d497523c002033d1 Reported-and-tested-by: Marcus Overhagen Reported-and-tested-by: Anders Kaseorg Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 3fcb6b3..f2b9780 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -428,14 +428,6 @@ static void elantech_report_trackpoint(struct psmouse *psmouse, int x, y; u32 t; - if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev, - !tp_dev, - psmouse_fmt("Unexpected trackpoint message\n"))) { - if (etd->debug == 1) - elantech_packet_dump(psmouse); - return; - } - t = get_unaligned_le32(&packet[0]); switch (t & ~7U) { @@ -793,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) unsigned char packet_type = packet[3] & 0x03; bool sanity_check; - if ((packet[3] & 0x0f) == 0x06) + if (etd->tp_dev && (packet[3] & 0x0f) == 0x06) return PACKET_TRACKPOINT; /* -- cgit v0.10.2 From a1f9a4072655843fc03186acbad65990cc05dd2d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 25 Nov 2014 00:38:17 -0800 Subject: Input: xpad - use proper endpoint type The xpad wireless endpoint is not a bulk endpoint on my devices, but rather an interrupt one, so the USB core complains when it is submitted. I'm guessing that the author really did mean that this should be an interrupt urb, but as there are a zillion different xpad devices out there, let's cover out bases and handle both bulk and interrupt endpoints just as easily. Signed-off-by: "Pierre-Loup A. Griffais" Signed-off-by: Greg Kroah-Hartman Cc: stable Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 2ed7905..fc55f0d 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1179,9 +1179,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id } ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; - usb_fill_bulk_urb(xpad->bulk_out, udev, - usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), - xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + if (usb_endpoint_is_bulk_out(ep_irq_in)) { + usb_fill_bulk_urb(xpad->bulk_out, udev, + usb_sndbulkpipe(udev, + ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, + xpad_bulk_out, xpad); + } else { + usb_fill_int_urb(xpad->bulk_out, udev, + usb_sndintpipe(udev, + ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, + xpad_bulk_out, xpad, 0); + } /* * Submit the int URB immediately rather than waiting for open -- cgit v0.10.2 From 138a7f49270fde7547afe976a01cef2b9fbf3a0e Mon Sep 17 00:00:00 2001 From: Andrew Lutomirski Date: Mon, 24 Nov 2014 12:02:29 -0800 Subject: net-timestamp: Fix a documentation typo SOF_TIMESTAMPING_OPT_ID puts the id in ee_data, not ee_info. Cc: Willem de Bruijn Signed-off-by: Andy Lutomirski Acked-by: Willem de Bruijn Signed-off-by: David S. Miller diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index 412f45c..1d6d02d 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -136,7 +136,7 @@ SOF_TIMESTAMPING_OPT_ID: This option is implemented only for transmit timestamps. There, the timestamp is always looped along with a struct sock_extended_err. - The option modifies field ee_info to pass an id that is unique + The option modifies field ee_data to pass an id that is unique among all possibly concurrently outstanding timestamp requests for that socket. In practice, it is a monotonically increasing u32 (that wraps). -- cgit v0.10.2 From f3750817a9034bd8cbb5ba583cd544e7cf14c7d8 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 24 Nov 2014 20:08:32 -0800 Subject: ip6_udp_tunnel: Fix checksum calculation The UDP checksum calculation for VXLAN tunnels is currently using the socket addresses instead of the actual packet source and destination addresses. As a result the checksum calculated is incorrect in some cases. Also uh->check was being set twice, first it was set to 0, and then it is set again in udp6_set_csum. This change removes the redundant assignment to 0. Fixes: acbf74a7 ("vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions.") Cc: Andy Zhou Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c index b04ed72..8db6c98 100644 --- a/net/ipv6/ip6_udp_tunnel.c +++ b/net/ipv6/ip6_udp_tunnel.c @@ -79,15 +79,13 @@ int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst, uh->source = src_port; uh->len = htons(skb->len); - uh->check = 0; memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); skb_dst_set(skb, dst); - udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr, - &sk->sk_v6_daddr, skb->len); + udp6_set_csum(udp_get_no_check6_tx(sk), skb, saddr, daddr, skb->len); __skb_push(skb, sizeof(*ip6h)); skb_reset_network_header(skb); -- cgit v0.10.2 From 3dc2b6a8d38cf6c7604ec25f3d50d6ec8da04435 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 24 Nov 2014 20:08:38 -0800 Subject: vxlan: Fix boolean flip in VXLAN_F_UDP_ZERO_CSUM6_[TX|RX] In "vxlan: Call udp_sock_create" there was a logic error that resulted in the default for IPv6 VXLAN tunnels going from using checksums to not using checksums. Since there is currently no support in iproute2 for setting these values it means that a kernel after the change cannot talk over a IPv6 VXLAN tunnel to a kernel prior the change. Fixes: 3ee64f3 ("vxlan: Call udp_sock_create") Cc: Tom Herbert Signed-off-by: Alexander Duyck Acked-by: Tom Herbert Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e1e335c..be4649a 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2306,9 +2306,9 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6, if (ipv6) { udp_conf.family = AF_INET6; udp_conf.use_udp6_tx_checksums = - !!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); + !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); udp_conf.use_udp6_rx_checksums = - !!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); + !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = INADDR_ANY; -- cgit v0.10.2 From 43612d7c04f1a4f5e60104143918fcdf018b66ee Mon Sep 17 00:00:00 2001 From: Pablo Neira Date: Tue, 25 Nov 2014 19:54:47 +0100 Subject: Revert "netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse" This reverts commit 5195c14c8b27cc0b18220ddbf0e5ad3328a04187. If the conntrack clashes with an existing one, it is left out of the unconfirmed list, thus, crashing when dropping the packet and releasing the conntrack since golden rule is that conntracks are always placed in any of the existing lists for traceability reasons. Reported-by: Daniel Borkmann Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=88841 Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 2c69975..5016a69 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -611,16 +611,12 @@ __nf_conntrack_confirm(struct sk_buff *skb) */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); pr_debug("Confirming conntrack %p\n", ct); - - /* We have to check the DYING flag after unlink to prevent - * a race against nf_ct_get_next_corpse() possibly called from - * user context, else we insert an already 'dead' hash, blocking - * further use of that particular connection -JM. - */ - nf_ct_del_from_dying_or_unconfirmed_list(ct); + /* We have to check the DYING flag inside the lock to prevent + a race against nf_ct_get_next_corpse() possibly called from + user context, else we insert an already 'dead' hash, blocking + further use of that particular connection -JM */ if (unlikely(nf_ct_is_dying(ct))) { - nf_ct_add_to_dying_list(ct); nf_conntrack_double_unlock(hash, reply_hash); local_bh_enable(); return NF_ACCEPT; @@ -640,6 +636,8 @@ __nf_conntrack_confirm(struct sk_buff *skb) zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) goto out; + nf_ct_del_from_dying_or_unconfirmed_list(ct); + /* Timer relative to confirmation time, not original setting time, otherwise we'd get timer wrap in weird delay cases. */ -- cgit v0.10.2 From a91ed1901a80b401afa1b718d941d3450d868151 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 25 Nov 2014 10:32:06 -0600 Subject: rtlwifi: rtl8821ae: Fix 5G detection problem The changes associated with moving this driver from staging to the regular tree missed one section setting the allowable rates for the 5GHz band. This patch is needed to fix the regression reported in Bug #88811 (https://bugzilla.kernel.org/show_bug.cgi?id=88811). Reported-by: Valerio Passini Tested-by: Valerio Passini Signed-off-by: Larry Finger Cc: Valerio Passini Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c index 310d316..8ec8200 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c @@ -3672,8 +3672,9 @@ static void rtl8821ae_update_hal_rate_mask(struct ieee80211_hw *hw, mac->opmode == NL80211_IFTYPE_ADHOC) macid = sta->aid + 1; if (wirelessmode == WIRELESS_MODE_N_5G || - wirelessmode == WIRELESS_MODE_AC_5G) - ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ]; + wirelessmode == WIRELESS_MODE_AC_5G || + wirelessmode == WIRELESS_MODE_A) + ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ] << 4; else ratr_bitmap = sta->supp_rates[NL80211_BAND_2GHZ]; -- cgit v0.10.2 From 7d63a5f9b25ba6b130da8eb2d32a72b1462d0249 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 25 Nov 2014 10:32:07 -0600 Subject: rtlwifi: Change order in device startup The existing order of steps when starting the PCI devices works for 2.4G devices, but fails to initialize the 5G section of the RTL8821AE hardware. This patch is needed to fix the regression reported in Bug #88811 (https://bugzilla.kernel.org/show_bug.cgi?id=88811). Reported-by: Valerio Passini Tested-by: Valerio Passini Signed-off-by: Larry Finger Cc: Valerio Passini Signed-off-by: John W. Linville diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 61f5d36..846a2e6 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -2249,6 +2249,16 @@ int rtl_pci_probe(struct pci_dev *pdev, /*like read eeprom and so on */ rtlpriv->cfg->ops->read_eeprom_info(hw); + if (rtlpriv->cfg->ops->init_sw_vars(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); + err = -ENODEV; + goto fail3; + } + rtlpriv->cfg->ops->init_sw_leds(hw); + + /*aspm */ + rtl_pci_init_aspm(hw); + /* Init mac80211 sw */ err = rtl_init_core(hw); if (err) { @@ -2264,16 +2274,6 @@ int rtl_pci_probe(struct pci_dev *pdev, goto fail3; } - if (rtlpriv->cfg->ops->init_sw_vars(hw)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n"); - err = -ENODEV; - goto fail3; - } - rtlpriv->cfg->ops->init_sw_leds(hw); - - /*aspm */ - rtl_pci_init_aspm(hw); - err = ieee80211_register_hw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, -- cgit v0.10.2 From c3658e8d0f10147fc86018be7f11668246c156d3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Nov 2014 07:40:04 -0800 Subject: tcp: fix possible NULL dereference in tcp_vX_send_reset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit ca777eff51f7 ("tcp: remove dst refcount false sharing for prequeue mode") we have to relax check against skb dst in tcp_v[46]_send_reset() if prequeue dropped the dst. If a socket is provided, a full lookup was done to find this socket, so the dst test can be skipped. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88191 Reported-by: JaÅ¡a Bartelj Signed-off-by: Eric Dumazet Reported-by: Daniel Borkmann Fixes: ca777eff51f7 ("tcp: remove dst refcount false sharing for prequeue mode") Signed-off-by: David S. Miller diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9c7d762..147be20 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -598,7 +598,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) if (th->rst) return; - if (skb_rtable(skb)->rt_type != RTN_LOCAL) + /* If sk not NULL, it means we did a successful lookup and incoming + * route had to be correct. prequeue might have dropped our dst. + */ + if (!sk && skb_rtable(skb)->rt_type != RTN_LOCAL) return; /* Swap the send and the receive. */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ace29b6..dc495ae 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -903,7 +903,10 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) if (th->rst) return; - if (!ipv6_unicast_destination(skb)) + /* If sk not NULL, it means we did a successful lookup and incoming + * route had to be correct. prequeue might have dropped our dst. + */ + if (!sk && !ipv6_unicast_destination(skb)) return; #ifdef CONFIG_TCP_MD5SIG -- cgit v0.10.2 From a620a6bc1c94c22d6c312892be1e0ae171523125 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 25 Nov 2014 14:21:11 -0200 Subject: tg3: fix ring init when there are more TX than RX channels If TX channels are set to 4 and RX channels are set to less than 4, using ethtool -L, the driver will try to initialize more RX channels than it has allocated, causing an oops. This fix only initializes the RX ring if it has been allocated. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index dbb41c1..77f8f83 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -8563,7 +8563,8 @@ static int tg3_init_rings(struct tg3 *tp) if (tnapi->rx_rcb) memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp)); - if (tg3_rx_prodring_alloc(tp, &tnapi->prodring)) { + if (tnapi->prodring.rx_std && + tg3_rx_prodring_alloc(tp, &tnapi->prodring)) { tg3_free_rings(tp); return -ENOMEM; } -- cgit v0.10.2 From a5e9ab291c608c62691b9d565104a30d931998bf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 25 Nov 2014 12:46:39 -0800 Subject: Revert "serial: of-serial: add PM suspend/resume support" This reverts commit 2dea53bf57783f243c892e99c10c6921e956aa7e. Turns out to be broken :( Cc: Jingchang Lu Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 56982da4..bf35505 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -240,32 +240,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int of_serial_suspend(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - serial8250_suspend_port(info->line); - if (info->clk) - clk_disable_unprepare(info->clk); - - return 0; -} - -static int of_serial_resume(struct device *dev) -{ - struct of_serial_info *info = dev_get_drvdata(dev); - - if (info->clk) - clk_prepare_enable(info->clk); - - serial8250_resume_port(info->line); - - return 0; -} -#endif -static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); - /* * A few common types, add more as needed. */ @@ -297,7 +271,6 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, - .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, -- cgit v0.10.2 From 714710e1a2f6528be83d0f1c6cdf1e79a676d234 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:16:14 -0800 Subject: irqchip: bcm7120-l2: Fix error handling of irq_of_parse_and_map Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Acked-by: Florian Fainelli Tested-by: Kevin Cernekee Link: https://lkml.kernel.org/r/20141114221614.GA37395@dtor-ws Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index b9f4fb8..5fb38a2 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -101,9 +101,9 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, int parent_irq; parent_irq = irq_of_parse_and_map(dn, irq); - if (parent_irq < 0) { + if (!parent_irq) { pr_err("failed to map interrupt %d\n", irq); - return parent_irq; + return -EINVAL; } data->irq_map_mask |= be32_to_cpup(map_mask + irq); -- cgit v0.10.2 From d99ba4465a08138966083d6c256b2f66e85a5095 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:16:42 -0800 Subject: irqchip: brcmstb-l2: Fix error handling of irq_of_parse_and_map Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Acked-by: Florian Fainelli Tested-by: Kevin Cernekee Link: https://lkml.kernel.org/r/20141114221642.GA37468@dtor-ws Signed-off-by: Jason Cooper diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index c15c840..14691a4 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -135,9 +135,9 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, __raw_writel(0xffffffff, data->base + CPU_CLEAR); data->parent_irq = irq_of_parse_and_map(np, 0); - if (data->parent_irq < 0) { + if (!data->parent_irq) { pr_err("failed to find parent interrupt\n"); - ret = data->parent_irq; + ret = -EINVAL; goto out_unmap; } -- cgit v0.10.2 From 97cd6805acf0d6b1b06b439cbc8c25586d73dcfc Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 24 Nov 2014 14:24:18 +0900 Subject: ufs: ensure clk gating work is finished before module unloading When dynamic clk gating feature is enabled, delayed workqueue machanism is used in order to detect certain period of inactivity. But there is no guarantee that scheduled gating work is completed before module unloading. So it can cause kernel crash by accessing memory after it was freed. Fix it by cancelling clk gating and ungating works and ensure that its execution is finished before module unloading. Signed-off-by: Akinobu Mita Reviewed-by: Subhash Jadavani Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b9da446..61bf002 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -744,6 +744,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) if (!ufshcd_is_clkgating_allowed(hba)) return; device_remove_file(hba->dev, &hba->clk_gating.delay_attr); + cancel_work_sync(&hba->clk_gating.ungate_work); + cancel_delayed_work_sync(&hba->clk_gating.gate_work); } /* Must be called with host lock acquired */ -- cgit v0.10.2 From 3e660fbef96f53d01ba022b1b75b72759a02983e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 18 Nov 2014 23:02:46 +0900 Subject: ufs: fix NULL dereference when no regulators are defined If no voltage supply regulators are defined for the UFS devices (assumed they are always-on), ufshcd_config_vreg_load() can be called on suspend/resume paths with vreg == NULL as hba->vreg_info.vcc* equal to NULL, and it causes NULL pointer dereference. This fixes it by making ufshcd_config_vreg_{h,l}pm noop when no regulators are defined. Signed-off-by: Akinobu Mita Reviewed-by: Subhash Jadavani Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 61bf002..605ca60 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4268,12 +4268,18 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { + if (!vreg) + return 0; + return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA); } static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { + if (!vreg) + return 0; + return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); } -- cgit v0.10.2 From afa4e53a7bcd4328d88e25c7a63746b65dc6bbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 25 Nov 2014 15:43:48 +0200 Subject: drm/i915: Cancel vdd off work before suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we just make sure vdd is off before suspending, but we don't cancel the vdd off work. The work wil not touch vdd if want_panel_vdd==false so in theory this is fine. In the past that was perfectly fine since the vdd off work didn't do anything when want_panel_vdd==false, so even if the work would have been run during system resume before i915 has resumed, nothing would happen. However since pps_lock() will now grab the power domain references before it can check want_panel_vdd, we may end up toggling the power wells on/off already before the driver has resumed. That is not really acceptable, so cancel the vdd off work when suspending the encoder. The problem appeared when pps_lock() was introduced in: commit 773538e86081d146e0020435d614f4b96996c1f9 Author: Ville Syrjälä Date: Thu Sep 4 14:54:56 2014 +0300 drm/i915: Reset power sequencer pipe tracking when disp2d is off Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5ad45bf..4bcd917 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4450,6 +4450,7 @@ static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) * vdd might still be enabled do to the delayed vdd off. * Make sure vdd is actually turned off here. */ + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); pps_unlock(intel_dp); -- cgit v0.10.2 From 7cbb87d67e38cfc55680290a706fd7517f10050d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 28 Oct 2014 19:36:45 +0000 Subject: arm64: KVM: fix unmapping with 48-bit VAs Currently if using a 48-bit VA, tearing down the hyp page tables (which can happen in the absence of a GICH or GICV resource) results in the rather nasty splat below, evidently becasue we access a table that doesn't actually exist. Commit 38f791a4e499792e (arm64: KVM: Implement 48 VA support for KVM EL2 and Stage-2) added a pgd_none check to __create_hyp_mappings to account for the additional level of tables, but didn't add a corresponding check to unmap_range, and this seems to be the source of the problem. This patch adds the missing pgd_none check, ensuring we don't try to access tables that don't exist. Original splat below: kvm [1]: Using HYP init bounce page @83fe94a000 kvm [1]: Cannot obtain GICH resource Unable to handle kernel paging request at virtual address ffff7f7fff000000 pgd = ffff800000770000 [ffff7f7fff000000] *pgd=0000000000000000 Internal error: Oops: 96000004 [#1] PREEMPT SMP Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc2+ #89 task: ffff8003eb500000 ti: ffff8003eb45c000 task.ti: ffff8003eb45c000 PC is at unmap_range+0x120/0x580 LR is at free_hyp_pgds+0xac/0xe4 pc : [] lr : [] pstate: 80000045 sp : ffff8003eb45fbf0 x29: ffff8003eb45fbf0 x28: ffff800000736000 x27: ffff800000735000 x26: ffff7f7fff000000 x25: 0000000040000000 x24: ffff8000006f5000 x23: 0000000000000000 x22: 0000007fffffffff x21: 0000800000000000 x20: 0000008000000000 x19: 0000000000000000 x18: ffff800000648000 x17: ffff800000537228 x16: 0000000000000000 x15: 000000000000001f x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000020 x11: 0000000000000062 x10: 0000000000000006 x9 : 0000000000000000 x8 : 0000000000000063 x7 : 0000000000000018 x6 : 00000003ff000000 x5 : ffff800000744188 x4 : 0000000000000001 x3 : 0000000040000000 x2 : ffff800000000000 x1 : 0000007fffffffff x0 : 000000003fffffff Process swapper/0 (pid: 1, stack limit = 0xffff8003eb45c058) Stack: (0xffff8003eb45fbf0 to 0xffff8003eb460000) fbe0: eb45fcb0 ffff8003 0009cad8 ffff8000 fc00: 00000000 00000080 00736140 ffff8000 00736000 ffff8000 00000000 00007c80 fc20: 00000000 00000080 006f5000 ffff8000 00000000 00000080 00743000 ffff8000 fc40: 00735000 ffff8000 006d3030 ffff8000 006fe7b8 ffff8000 00000000 00000080 fc60: ffffffff 0000007f fdac1000 ffff8003 fd94b000 ffff8003 fda47000 ffff8003 fc80: 00502b40 ffff8000 ff000000 ffff7f7f fdec6000 00008003 fdac1630 ffff8003 fca0: eb45fcb0 ffff8003 ffffffff 0000007f eb45fd00 ffff8003 0009b378 ffff8000 fcc0: ffffffea 00000000 006fe000 ffff8000 00736728 ffff8000 00736120 ffff8000 fce0: 00000040 00000000 00743000 ffff8000 006fe7b8 ffff8000 0050cd48 00000000 fd00: eb45fd60 ffff8003 00096070 ffff8000 006f06e0 ffff8000 006f06e0 ffff8000 fd20: fd948b40 ffff8003 0009a320 ffff8000 00000000 00000000 00000000 00000000 fd40: 00000ae0 00000000 006aa25c ffff8000 eb45fd60 ffff8003 0017ca44 00000002 fd60: eb45fdc0 ffff8003 0009a33c ffff8000 006f06e0 ffff8000 006f06e0 ffff8000 fd80: fd948b40 ffff8003 0009a320 ffff8000 00000000 00000000 00735000 ffff8000 fda0: 006d3090 ffff8000 006aa25c ffff8000 00735000 ffff8000 006d3030 ffff8000 fdc0: eb45fdd0 ffff8003 000814c0 ffff8000 eb45fe50 ffff8003 006aaac4 ffff8000 fde0: 006ddd90 ffff8000 00000006 00000000 006d3000 ffff8000 00000095 00000000 fe00: 006a1e90 ffff8000 00735000 ffff8000 006d3000 ffff8000 006aa25c ffff8000 fe20: 00735000 ffff8000 006d3030 ffff8000 eb45fe50 ffff8003 006fac68 ffff8000 fe40: 00000006 00000006 fe293ee6 ffff8003 eb45feb0 ffff8003 004f8ee8 ffff8000 fe60: 004f8ed4 ffff8000 00735000 ffff8000 00000000 00000000 00000000 00000000 fe80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 fea0: 00000000 00000000 00000000 00000000 00000000 00000000 000843d0 ffff8000 fec0: 004f8ed4 ffff8000 00000000 00000000 00000000 00000000 00000000 00000000 fee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ff80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ffa0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000005 00000000 ffe0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Call trace: [] unmap_range+0x120/0x580 [] free_hyp_pgds+0xa8/0xe4 [] kvm_arch_init+0x268/0x44c [] kvm_init+0x24/0x260 [] arm_init+0x18/0x24 [] do_one_initcall+0x88/0x1a0 [] kernel_init_freeable+0x148/0x1e8 [] kernel_init+0x10/0xd4 Code: 8b000263 92628479 d1000720 eb01001f (f9400340) ---[ end trace 3bc230562e926fa4 ]--- Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Signed-off-by: Mark Rutland Cc: Catalin Marinas Cc: Jungseok Lee Acked-by: Marc Zyngier Acked-by: Christoffer Dall Signed-off-by: Marc Zyngier Signed-off-by: Paolo Bonzini diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 57a403a..79d3fbf 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -197,7 +197,8 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp, pgd = pgdp + pgd_index(addr); do { next = kvm_pgd_addr_end(addr, end); - unmap_puds(kvm, pgd, addr, next); + if (!pgd_none(*pgd)) + unmap_puds(kvm, pgd, addr, next); } while (pgd++, addr = next, addr != end); } -- cgit v0.10.2 From db7dedd0de714a59d8fa2a1be3b6b00543ad415a Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Wed, 19 Nov 2014 11:23:54 +0000 Subject: arm64: KVM: Handle traps of ICC_SRE_EL1 as RAZ/WI When running on a system with a GICv3, we currenly don't allow the guest to access the system register interface of the GICv3. We do this by clearing the ICC_SRE_EL2.Enable, which causes all guest accesses to ICC_SRE_EL1 to trap to EL2 and causes all guest accesses to other ICC_ registers to cause an undefined exception in the guest. However, we currently don't handle the trap of guest accesses to ICC_SRE_EL1 and will spill out a warning. The trap just needs to handle the access as RAZ/WI, and a guest that tries to prod this register and set ICC_SRE_EL1.SRE=1, must read back the value (which Linux already does) to see if it succeeded, and will thus observe that ICC_SRE_EL1.SRE was not set. Add the simple trap handler in the sorted table of the system registers. Signed-off-by: Christoffer Dall [ardb: added cp15 handling] Signed-off-by: Ard Biesheuvel Signed-off-by: Marc Zyngier Signed-off-by: Paolo Bonzini diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 4cc3b71..3d7c2df 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -424,6 +424,11 @@ static const struct sys_reg_desc sys_reg_descs[] = { /* VBAR_EL1 */ { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000), NULL, reset_val, VBAR_EL1, 0 }, + + /* ICC_SRE_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b1100), Op2(0b101), + trap_raz_wi }, + /* CONTEXTIDR_EL1 */ { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 }, @@ -690,6 +695,10 @@ static const struct sys_reg_desc cp15_regs[] = { { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR }, { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 }, { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 }, + + /* ICC_SRE */ + { Op1( 0), CRn(12), CRm(12), Op2( 5), trap_raz_wi }, + { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID }, }; -- cgit v0.10.2 From 6b50f54064a02b77a7b990032b80234fee59bcd6 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Thu, 6 Nov 2014 11:47:39 +0000 Subject: arm/arm64: KVM: vgic: Fix error code in kvm_vgic_create() If we detect another vCPU is running we just exit and return 0 as if we succesfully created the VGIC, but the VGIC wouldn't actual be created. This shouldn't break in-kernel behavior because the kernel will not observe the failed the attempt to create the VGIC, but userspace could be rightfully confused. Cc: Andre Przywara Signed-off-by: Christoffer Dall Signed-off-by: Marc Zyngier Signed-off-by: Paolo Bonzini diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 3aaca49..aacdb59 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1933,7 +1933,7 @@ out: int kvm_vgic_create(struct kvm *kvm) { - int i, vcpu_lock_idx = -1, ret = 0; + int i, vcpu_lock_idx = -1, ret; struct kvm_vcpu *vcpu; mutex_lock(&kvm->lock); @@ -1948,6 +1948,7 @@ int kvm_vgic_create(struct kvm *kvm) * vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure * that no other VCPUs are run while we create the vgic. */ + ret = -EBUSY; kvm_for_each_vcpu(i, vcpu, kvm) { if (!mutex_trylock(&vcpu->mutex)) goto out_unlock; @@ -1955,11 +1956,10 @@ int kvm_vgic_create(struct kvm *kvm) } kvm_for_each_vcpu(i, vcpu, kvm) { - if (vcpu->arch.has_run_once) { - ret = -EBUSY; + if (vcpu->arch.has_run_once) goto out_unlock; - } } + ret = 0; spin_lock_init(&kvm->arch.vgic.lock); kvm->arch.vgic.in_kernel = true; -- cgit v0.10.2 From bb55e9b131d70ab9e30d73ab1342ad4907f9e0de Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Nov 2014 09:33:55 +0100 Subject: arm/arm64: kvm: drop inappropriate use of kvm_is_mmio_pfn() Instead of using kvm_is_mmio_pfn() to decide whether a host region should be stage 2 mapped with device attributes, add a new static function kvm_is_device_pfn() that disregards RAM pages with the reserved bit set, as those should usually not be mapped as device memory. Signed-off-by: Ard Biesheuvel Signed-off-by: Paolo Bonzini diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 79d3fbf..8664ff1 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -835,6 +835,11 @@ static bool kvm_is_write_fault(struct kvm_vcpu *vcpu) return kvm_vcpu_dabt_iswrite(vcpu); } +static bool kvm_is_device_pfn(unsigned long pfn) +{ + return !pfn_valid(pfn); +} + static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, struct kvm_memory_slot *memslot, unsigned long hva, unsigned long fault_status) @@ -905,7 +910,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, if (is_error_pfn(pfn)) return -EFAULT; - if (kvm_is_mmio_pfn(pfn)) + if (kvm_is_device_pfn(pfn)) mem_type = PAGE_S2_DEVICE; spin_lock(&kvm->mmu_lock); -- cgit v0.10.2 From d3fccc7ef831d1d829b4da5eaa081db55b1e38f3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Nov 2014 09:33:56 +0100 Subject: kvm: fix kvm_is_mmio_pfn() and rename to kvm_is_reserved_pfn() This reverts commit 85c8555ff0 ("KVM: check for !is_zero_pfn() in kvm_is_mmio_pfn()") and renames the function to kvm_is_reserved_pfn. The problem being addressed by the patch above was that some ARM code based the memory mapping attributes of a pfn on the return value of kvm_is_mmio_pfn(), whose name indeed suggests that such pfns should be mapped as device memory. However, kvm_is_mmio_pfn() doesn't do quite what it says on the tin, and the existing non-ARM users were already using it in a way which suggests that its name should probably have been 'kvm_is_reserved_pfn' from the beginning, e.g., whether or not to call get_page/put_page on it etc. This means that returning false for the zero page is a mistake and the patch above should be reverted. Signed-off-by: Ard Biesheuvel Signed-off-by: Paolo Bonzini diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index ec6b9ac..dbe46f4 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1563,7 +1563,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, for (i = 0; i < npages; i++) { pfn = gfn_to_pfn(kvm, base_gfn + i); - if (!kvm_is_mmio_pfn(pfn)) { + if (!kvm_is_reserved_pfn(pfn)) { kvm_set_pmt_entry(kvm, base_gfn + i, pfn << PAGE_SHIFT, _PAGE_AR_RWX | _PAGE_MA_WB); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index ac1c4de..978f402 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -630,7 +630,7 @@ static int mmu_spte_clear_track_bits(u64 *sptep) * kvm mmu, before reclaiming the page, we should * unmap it from mmu first. */ - WARN_ON(!kvm_is_mmio_pfn(pfn) && !page_count(pfn_to_page(pfn))); + WARN_ON(!kvm_is_reserved_pfn(pfn) && !page_count(pfn_to_page(pfn))); if (!shadow_accessed_mask || old_spte & shadow_accessed_mask) kvm_set_pfn_accessed(pfn); @@ -2461,7 +2461,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |= PT_PAGE_SIZE_MASK; if (tdp_enabled) spte |= kvm_x86_ops->get_mt_mask(vcpu, gfn, - kvm_is_mmio_pfn(pfn)); + kvm_is_reserved_pfn(pfn)); if (host_writable) spte |= SPTE_HOST_WRITEABLE; @@ -2737,7 +2737,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu, * PT_PAGE_TABLE_LEVEL and there would be no adjustment done * here. */ - if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn) && + if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) && level == PT_PAGE_TABLE_LEVEL && PageTransCompound(pfn_to_page(pfn)) && !has_wrprotected_page(vcpu->kvm, gfn, PT_DIRECTORY_LEVEL)) { diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ea53b04..a6059bd 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -703,7 +703,7 @@ void kvm_arch_sync_events(struct kvm *kvm); int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); void kvm_vcpu_kick(struct kvm_vcpu *vcpu); -bool kvm_is_mmio_pfn(pfn_t pfn); +bool kvm_is_reserved_pfn(pfn_t pfn); struct kvm_irq_ack_notifier { struct hlist_node link; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 25ffac9..3cee7b1 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -107,10 +107,10 @@ EXPORT_SYMBOL_GPL(kvm_rebooting); static bool largepages_enabled = true; -bool kvm_is_mmio_pfn(pfn_t pfn) +bool kvm_is_reserved_pfn(pfn_t pfn) { if (pfn_valid(pfn)) - return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn)); + return PageReserved(pfn_to_page(pfn)); return true; } @@ -1321,7 +1321,7 @@ static pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async, else if ((vma->vm_flags & VM_PFNMAP)) { pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; - BUG_ON(!kvm_is_mmio_pfn(pfn)); + BUG_ON(!kvm_is_reserved_pfn(pfn)); } else { if (async && vma_is_valid(vma, write_fault)) *async = true; @@ -1427,7 +1427,7 @@ static struct page *kvm_pfn_to_page(pfn_t pfn) if (is_error_noslot_pfn(pfn)) return KVM_ERR_PTR_BAD_PAGE; - if (kvm_is_mmio_pfn(pfn)) { + if (kvm_is_reserved_pfn(pfn)) { WARN_ON(1); return KVM_ERR_PTR_BAD_PAGE; } @@ -1456,7 +1456,7 @@ EXPORT_SYMBOL_GPL(kvm_release_page_clean); void kvm_release_pfn_clean(pfn_t pfn) { - if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn)) + if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn)) put_page(pfn_to_page(pfn)); } EXPORT_SYMBOL_GPL(kvm_release_pfn_clean); @@ -1477,7 +1477,7 @@ static void kvm_release_pfn_dirty(pfn_t pfn) void kvm_set_pfn_dirty(pfn_t pfn) { - if (!kvm_is_mmio_pfn(pfn)) { + if (!kvm_is_reserved_pfn(pfn)) { struct page *page = pfn_to_page(pfn); if (!PageReserved(page)) SetPageDirty(page); @@ -1487,14 +1487,14 @@ EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty); void kvm_set_pfn_accessed(pfn_t pfn) { - if (!kvm_is_mmio_pfn(pfn)) + if (!kvm_is_reserved_pfn(pfn)) mark_page_accessed(pfn_to_page(pfn)); } EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed); void kvm_get_pfn(pfn_t pfn) { - if (!kvm_is_mmio_pfn(pfn)) + if (!kvm_is_reserved_pfn(pfn)) get_page(pfn_to_page(pfn)); } EXPORT_SYMBOL_GPL(kvm_get_pfn); -- cgit v0.10.2 From a566059d89139c3c3ce21b0c7cb37886b2549b78 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 25 Nov 2014 18:08:48 -0800 Subject: net: dsa: bcm_sf2: fix unmapping registers in case of errors In case we fail to ioremap() one of our registers, we would be leaking existing mappings, unwind those accordingly on errors. Fixes: 246d7f773c13 ("net: dsa: add Broadcom SF2 switch driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index b962596..46632e8 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -404,7 +404,8 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) *base = of_iomap(dn, i); if (*base == NULL) { pr_err("unable to find register: %s\n", reg_names[i]); - return -ENODEV; + ret = -ENOMEM; + goto out_unmap; } base++; } @@ -484,7 +485,8 @@ out_free_irq0: out_unmap: base = &priv->core; for (i = 0; i < BCM_SF2_REGS_NUM; i++) { - iounmap(*base); + if (*base) + iounmap(*base); base++; } return ret; -- cgit v0.10.2 From 33f846142919f0ecceb9fde99fecdbc307dbf541 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 25 Nov 2014 18:08:49 -0800 Subject: net: dsa: bcm_sf2: reset switch prior to initialization Our boot agent may have left the switch in an certain configuration state, make sure we issue a software reset prior to configuring the switch in order to ensure the HW is in a consistent state, in particular transmit queues and internal buffers. Fixes: 246d7f773c13 ("net: dsa: add Broadcom SF2 switch driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 46632e8..4f4c2a7 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -377,6 +377,29 @@ static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) +{ + unsigned int timeout = 1000; + u32 reg; + + reg = core_readl(priv, CORE_WATCHDOG_CTRL); + reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET; + core_writel(priv, reg, CORE_WATCHDOG_CTRL); + + do { + reg = core_readl(priv, CORE_WATCHDOG_CTRL); + if (!(reg & SOFTWARE_RESET)) + break; + + usleep_range(1000, 2000); + } while (timeout-- > 0); + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} + static int bcm_sf2_sw_setup(struct dsa_switch *ds) { const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; @@ -410,6 +433,12 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) base++; } + ret = bcm_sf2_sw_rst(priv); + if (ret) { + pr_err("unable to software reset switch: %d\n", ret); + goto out_unmap; + } + /* Disable all interrupts and request them */ intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); @@ -735,29 +764,6 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) return 0; } -static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) -{ - unsigned int timeout = 1000; - u32 reg; - - reg = core_readl(priv, CORE_WATCHDOG_CTRL); - reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET; - core_writel(priv, reg, CORE_WATCHDOG_CTRL); - - do { - reg = core_readl(priv, CORE_WATCHDOG_CTRL); - if (!(reg & SOFTWARE_RESET)) - break; - - usleep_range(1000, 2000); - } while (timeout-- > 0); - - if (timeout == 0) - return -ETIMEDOUT; - - return 0; -} - static int bcm_sf2_sw_resume(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = ds_to_priv(ds); -- cgit v0.10.2 From 2d5c57d7fbfaa642fb7f0673df24f32b83d9066c Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 25 Nov 2014 11:54:31 +0200 Subject: net/mlx4_core: Limit count field to 24 bits in qp_alloc_res Some VF drivers use the upper byte of "param1" (the qp count field) in mlx4_qp_reserve_range() to pass flags which are used to optimize the range allocation. Under the current code, if any of these flags are set, the 32-bit count field yields a count greater than 2^24, which is out of range, and this VF fails. As these flags represent a "best-effort" allocation hint anyway, they may safely be ignored. Therefore, the PF driver may simply mask out the bits. Fixes: c82e9aa0a8 "mlx4_core: resource tracking for HCA resources used by guests" Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 5d2498d..cd5cf6d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1546,7 +1546,7 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, switch (op) { case RES_OP_RESERVE: - count = get_param_l(&in_param); + count = get_param_l(&in_param) & 0xffffff; align = get_param_h(&in_param); err = mlx4_grant_resource(dev, slave, RES_QP, count, 0); if (err) -- cgit v0.10.2 From 571dcfde23712b92e45a126f415d9424af0c2886 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 26 Nov 2014 10:38:06 +0800 Subject: stmmac: platform: fix default values of the filter bins setting The commit 3b57de958e2a brought the support for a different amount of the filter bins, but didn't update the platform driver that without CONFIG_OF. Fixes: 3b57de958e2a (net: stmmac: Support devicetree configs for mcast and ucast filter entries) Signed-off-by: Huacai Chen Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index db56fa7..5b0da39 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -177,12 +177,6 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, */ plat->maxmtu = JUMBO_LEN; - /* Set default value for multicast hash bins */ - plat->multicast_filter_bins = HASH_TABLE_SIZE; - - /* Set default value for unicast filter entries */ - plat->unicast_filter_entries = 1; - /* * Currently only the properties needed on SPEAr600 * are provided. All other properties should be added @@ -270,6 +264,13 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) return PTR_ERR(addr); plat_dat = dev_get_platdata(&pdev->dev); + + /* Set default value for multicast hash bins */ + plat_dat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ + plat_dat->unicast_filter_entries = 1; + if (pdev->dev.of_node) { if (!plat_dat) plat_dat = devm_kzalloc(&pdev->dev, -- cgit v0.10.2 From 6e8d1c55454574a22b3e8e263b1a12888909b033 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Nov 2014 13:42:16 +0100 Subject: bridge: Validate IFLA_BRIDGE_FLAGS attribute length Payload is currently accessed blindly and may exceed valid message boundaries. Fixes: 407af3299 ("bridge: Add netlink interface to configure vlans on bridge ports") Cc: Vlad Yasevich Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a688268..5a853f8 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2798,6 +2798,9 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) if (br_spec) { nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + if (nla_len(attr) < sizeof(flags)) + return -EINVAL; + have_flags = true; flags = nla_get_u16(attr); break; @@ -2868,6 +2871,9 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) if (br_spec) { nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + if (nla_len(attr) < sizeof(flags)) + return -EINVAL; + have_flags = true; flags = nla_get_u16(attr); break; -- cgit v0.10.2 From b7c1a314112785c319b2ba2dc8e73497714e42a1 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Nov 2014 13:42:17 +0100 Subject: net: Validate IFLA_BRIDGE_MODE attribute length Payload is currently accessed blindly and may exceed valid message boundaries. Fixes: a77dcb8c8 ("be2net: set and query VEB/VEPA mode of the PF interface") Fixes: 815cccbf1 ("ixgbe: add setlink, getlink support to ixgbe and ixgbevf") Cc: Ajit Khaparde Cc: John Fastabend Signed-off-by: Thomas Graf Acked-by: Jeff Kirsher Acked-by: John Fastabend Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3e8475c..337e4cd 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4314,6 +4314,9 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh) if (nla_type(attr) != IFLA_BRIDGE_MODE) continue; + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + mode = nla_get_u16(attr); if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB) return -EINVAL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 82ffe8b..dff9905 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7677,6 +7677,9 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, if (nla_type(attr) != IFLA_BRIDGE_MODE) continue; + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + mode = nla_get_u16(attr); if (mode == BRIDGE_MODE_VEPA) { reg = 0; -- cgit v0.10.2 From 4ea85e831e290cd967d161c66d0a3cf8be39f1f6 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Nov 2014 13:42:18 +0100 Subject: net: Check for presence of IFLA_AF_SPEC ndo_bridge_setlink() is currently only called on the slave if IFLA_AF_SPEC is set but this is a very fragile assumption and may change in the future. Cc: Ajit Khaparde Cc: John Fastabend Signed-off-by: Thomas Graf Acked-by: Jeff Kirsher Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 337e4cd..597c463 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4309,6 +4309,8 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh) return -EOPNOTSUPP; br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) != IFLA_BRIDGE_MODE) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index dff9905..cc51554 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7669,6 +7669,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, return -EOPNOTSUPP; br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; nla_for_each_nested(attr, br_spec, rem) { __u16 mode; -- cgit v0.10.2 From 6f705d8cfc0af3607aed890c53d86255e315c6f4 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Nov 2014 13:42:19 +0100 Subject: bridge: Add missing policy entry for IFLA_BRPORT_FAST_LEAVE Fixes: c2d3babf ("bridge: implement multicast fast leave") Cc: David S. Miller Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 2ff9706..e5ec470 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -280,6 +280,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 }, [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, }; -- cgit v0.10.2 From aa68c20ff32f9a6fb3ca7f93ed9beae01899d00d Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 26 Nov 2014 13:42:20 +0100 Subject: bridge: Sanitize IFLA_EXT_MASK for AF_BRIDGE:RTM_GETLINK Only search for IFLA_EXT_MASK if the message actually carries a ifinfomsg header and validate minimal length requirements for IFLA_EXT_MASK. Fixes: 6cbdceeb ("bridge: Dump vlan information from a bridge port") Cc: Vlad Yasevich Signed-off-by: Thomas Graf Signed-off-by: David S. Miller diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 5a853f8..b9b7dfa 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2685,13 +2685,20 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) int idx = 0; u32 portid = NETLINK_CB(cb->skb).portid; u32 seq = cb->nlh->nlmsg_seq; - struct nlattr *extfilt; u32 filter_mask = 0; - extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), - IFLA_EXT_MASK); - if (extfilt) - filter_mask = nla_get_u32(extfilt); + if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) { + struct nlattr *extfilt; + + extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), + IFLA_EXT_MASK); + if (extfilt) { + if (nla_len(extfilt) < sizeof(filter_mask)) + return -EINVAL; + + filter_mask = nla_get_u32(extfilt); + } + } rcu_read_lock(); for_each_netdev_rcu(net, dev) { -- cgit v0.10.2 From f4713a3dfad045d46afcb9c2a7d0bba288920ed4 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 26 Nov 2014 14:53:02 -0500 Subject: net-timestamp: make tcp_recvmsg call ipv6_recv_error for AF_INET6 socks TCP timestamping introduced MSG_ERRQUEUE handling for TCP sockets. If the socket is of family AF_INET6, call ipv6_recv_error instead of ip_recv_error. This change is more complex than a single branch due to the loadable ipv6 module. It reuses a pre-existing indirect function call from ping. The ping code is safe to call, because it is part of the core ipv6 module and always present when AF_INET6 sockets are active. Fixes: 4ed2d765 (net-timestamp: TCP timestamping) Signed-off-by: Willem de Bruijn ---- It may also be worthwhile to add WARN_ON_ONCE(sk->family == AF_INET6) to ip_recv_error. Signed-off-by: David S. Miller diff --git a/include/net/inet_common.h b/include/net/inet_common.h index fe7994c..b2828a0 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -37,6 +37,8 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int inet_ctl_sock_create(struct sock **sk, unsigned short family, unsigned short type, unsigned char protocol, struct net *net); +int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, + int *addr_len); static inline void inet_ctl_sock_destroy(struct sock *sk) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 8b7fe5b..e67da4e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1386,6 +1386,17 @@ out: return pp; } +int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) +{ + if (sk->sk_family == AF_INET) + return ip_recv_error(sk, msg, len, addr_len); +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) + return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len); +#endif + return -EINVAL; +} + static int inet_gro_complete(struct sk_buff *skb, int nhoff) { __be16 newlen = htons(skb->len - nhoff); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 85a02a7..5d740cc 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -855,16 +855,8 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (flags & MSG_OOB) goto out; - if (flags & MSG_ERRQUEUE) { - if (family == AF_INET) { - return ip_recv_error(sk, msg, len, addr_len); -#if IS_ENABLED(CONFIG_IPV6) - } else if (family == AF_INET6) { - return pingv6_ops.ipv6_recv_error(sk, msg, len, - addr_len); -#endif - } - } + if (flags & MSG_ERRQUEUE) + return inet_recv_error(sk, msg, len, addr_len); skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 39ec0c3..38c2bcb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1598,7 +1598,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, u32 urg_hole = 0; if (unlikely(flags & MSG_ERRQUEUE)) - return ip_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len, addr_len); if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) && (sk->sk_state == TCP_ESTABLISHED)) -- cgit v0.10.2 From 0d1d9092b536859a333ee96cff4df0ee24b55190 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 26 Nov 2014 13:36:41 -0800 Subject: sparc: Add NOP dma_cache_sync() implementation. This can be a NOP because we forward dma_{alloc,free}_noncoherent to dma_{alloc,free}_coherent. Signed-off-by: David S. Miller diff --git a/arch/sparc/include/asm/dma-mapping.h b/arch/sparc/include/asm/dma-mapping.h index 5b1b52a..7e064c6 100644 --- a/arch/sparc/include/asm/dma-mapping.h +++ b/arch/sparc/include/asm/dma-mapping.h @@ -12,6 +12,14 @@ int dma_supported(struct device *dev, u64 mask); #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction dir) +{ + /* Since dma_{alloc,free}_noncoherent() allocated coherent memory, this + * routine can be a nop. + */ +} + extern struct dma_map_ops *dma_ops; extern struct dma_map_ops *leon_dma_ops; extern struct dma_map_ops pci32_dma_ops; -- cgit v0.10.2 From 33dc85c3c667209c930b2dac5ccbc2a365e06b7a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 12 Nov 2014 10:07:49 -0600 Subject: staging: r8188eu: Fix scheduling while atomic error introduced in commit fadbe0cd In commit fadbe0cd5292851608e2e01b91d9295fa287b9fe entitled "staging: rtl8188eu:Remove rtw_zmalloc(), wrapper for kzalloc()", the author failed to note that the original code in the wrapper tested whether the caller could sleep, and set the flags argument to kzalloc() appropriately. After the patch, GFP_KERNEL is used unconditionally. Unfortunately, several of the routines may be entered from an interrupt routine and generate a BUG splat for every such call. Routine rtw_sitesurvey_cmd() is used in the example below: BUG: sleeping function called from invalid context at mm/slub.c:1240 in_atomic(): 1, irqs_disabled(): 0, pid: 756, name: wpa_supplicant INFO: lockdep is turned off. CPU: 2 PID: 756 Comm: wpa_supplicant Tainted: G WC O 3.18.0-rc4+ #34 Hardware name: TOSHIBA TECRA A50-A/TECRA A50-A, BIOS Version 4.20 04/17/2014 ffffc90005557000 ffff880216fafaa8 ffffffff816b0bbf 0000000000000000 ffff8800c3b58000 ffff880216fafac8 ffffffff8107af77 0000000000000001 0000000000000010 ffff880216fafb18 ffffffff811b06ce 0000000000000000 Call Trace: [] dump_stack+0x4e/0x71 [] __might_sleep+0xf7/0x120 [] kmem_cache_alloc_trace+0x4e/0x1f0 [] ? rtw_sitesurvey_cmd+0x56/0x2a0 [r8188eu] [] rtw_sitesurvey_cmd+0x56/0x2a0 [r8188eu] [] rtw_do_join+0x22d/0x370 [r8188eu] [] rtw_set_802_11_ssid+0x218/0x3d0 [r8188eu] [] rtw_wx_set_essid+0x1e5/0x410 [r8188eu] [] ? rtw_wx_get_rate+0x50/0x50 [r8188eu] [] ioctl_standard_iw_point+0x151/0x3f0 [] ioctl_standard_call+0xb2/0xe0 [] ? rtnl_lock+0x17/0x20 [] ? iw_handler_get_private+0x70/0x70 [] ? call_commit_handler+0x40/0x40 [] wireless_process_ioctl+0x176/0x1c0 [] wext_handle_ioctl+0x69/0xc0 [] dev_ioctl+0x309/0x5e0 [] ? call_rcu+0x17/0x20 [] sock_ioctl+0x142/0x2e0 [] do_vfs_ioctl+0x300/0x520 [] ? __audit_syscall_entry+0xb4/0x110 [] ? __audit_syscall_entry+0xb4/0x110 [] ? do_audit_syscall_entry+0x6c/0x70 [] SyS_ioctl+0x81/0xa0 [] system_call_fastpath+0x12/0x17 Additional routines that generate this BUG are rtw_joinbss_cmd(), rtw_dynamic_chk_wk_cmd(), rtw_lps_ctrl_wk_cmd(), rtw_rpt_timer_cfg_cmd(), rtw_ps_cmd(), report_survey_event(), report_join_res(), survey_timer_hdl(), and rtw_check_bcn_info(). Signed-off-by: Larry Finger Cc: navin patidar Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c index 9935e66..eddef9c 100644 --- a/drivers/staging/rtl8188eu/core/rtw_cmd.c +++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c @@ -275,11 +275,11 @@ u8 rtw_sitesurvey_cmd(struct adapter *padapter, struct ndis_802_11_ssid *ssid, if (check_fwstate(pmlmepriv, _FW_LINKED) == true) rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 1); - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) return _FAIL; - psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL); + psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC); if (psurveyPara == NULL) { kfree(ph2c); return _FAIL; @@ -405,7 +405,7 @@ u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork) else RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+Join cmd: SSid =[%s]\n", pmlmepriv->assoc_ssid.Ssid)); - pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd == NULL) { res = _FAIL; RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd: memory allocate for cmd_obj fail!!!\n")); @@ -755,13 +755,13 @@ u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter) u8 res = _SUCCESS; - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -967,13 +967,13 @@ u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue) u8 res = _SUCCESS; if (enqueue) { - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -1010,13 +1010,13 @@ u8 rtw_rpt_timer_cfg_cmd(struct adapter *padapter, u16 min_time) u8 res = _SUCCESS; - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ph2c); res = _FAIL; @@ -1088,13 +1088,13 @@ u8 rtw_ps_cmd(struct adapter *padapter) u8 res = _SUCCESS; - ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ppscmd == NULL) { res = _FAIL; goto exit; } - pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL); + pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC); if (pdrvextra_cmd_parm == NULL) { kfree(ppscmd); res = _FAIL; diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c index 5ba5099..70b1bc3 100644 --- a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c @@ -4241,12 +4241,12 @@ void report_survey_event(struct adapter *padapter, pcmdpriv = &padapter->cmdpriv; - pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd_obj == NULL) return; cmdsz = (sizeof(struct survey_event) + sizeof(struct C2HEvent_Header)); - pevtcmd = kzalloc(cmdsz, GFP_KERNEL); + pevtcmd = kzalloc(cmdsz, GFP_ATOMIC); if (pevtcmd == NULL) { kfree(pcmd_obj); return; @@ -4339,12 +4339,12 @@ void report_join_res(struct adapter *padapter, int res) struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info); struct cmd_priv *pcmdpriv = &padapter->cmdpriv; - pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (pcmd_obj == NULL) return; cmdsz = (sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header)); - pevtcmd = kzalloc(cmdsz, GFP_KERNEL); + pevtcmd = kzalloc(cmdsz, GFP_ATOMIC); if (pevtcmd == NULL) { kfree(pcmd_obj); return; @@ -4854,11 +4854,11 @@ void survey_timer_hdl(void *function_context) pmlmeext->scan_abort = false;/* reset */ } - ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL); + ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC); if (ph2c == NULL) goto exit_survey_timer_hdl; - psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL); + psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC); if (psurveyPara == NULL) { kfree(ph2c); goto exit_survey_timer_hdl; diff --git a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c index 33ccbbb..d300369 100644 --- a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c @@ -935,7 +935,7 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len) return true; } - bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_KERNEL); + bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC); subtype = GetFrameSubType(pframe) >> 4; -- cgit v0.10.2 From 6acbc5a1dab30aa8f7be7bf3852f343f89147ac5 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 20 Nov 2014 09:44:36 +0530 Subject: powerpc/powernv: Fix the hmi event version check. The current HMI event structure is an ABI and carries a version field to accommodate future changes without affecting/rearranging current structure members that are valid for previous versions. The current version check "if (hmi_evt->version != OpalHMIEvt_V1)" doesn't accomodate the fact that the version number may change in future. If firmware starts returning an HMI event with version > 1, this check will fail and no HMI information will be printed on older kernels. This patch fixes this issue. Cc: stable@vger.kernel.org # 3.17+ Signed-off-by: Mahesh Salgaonkar [mpe: Reword changelog] Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/platforms/powernv/opal-hmi.c b/arch/powerpc/platforms/powernv/opal-hmi.c index 5e1ed15..b322bfb 100644 --- a/arch/powerpc/platforms/powernv/opal-hmi.c +++ b/arch/powerpc/platforms/powernv/opal-hmi.c @@ -57,7 +57,7 @@ static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt) }; /* Print things out */ - if (hmi_evt->version != OpalHMIEvt_V1) { + if (hmi_evt->version < OpalHMIEvt_V1) { pr_err("HMI Interrupt, Unknown event version %d !\n", hmi_evt->version); return; -- cgit v0.10.2 From 3b8a3c01096925a824ed3272601082289d9c23a5 Mon Sep 17 00:00:00 2001 From: Laurent Dufour Date: Mon, 24 Nov 2014 15:07:53 +0100 Subject: powerpc/pseries: Fix endiannes issue in RTAS call from xmon On pseries system (LPAR) xmon failed to enter when running in LE mode, system is hunging. Inititating xmon will lead to such an output on the console: SysRq : Entering xmon cpu 0x15: Vector: 0 at [c0000003f39ffb10] pc: c00000000007ed7c: sysrq_handle_xmon+0x5c/0x70 lr: c00000000007ed7c: sysrq_handle_xmon+0x5c/0x70 sp: c0000003f39ffc70 msr: 8000000000009033 current = 0xc0000003fafa7180 paca = 0xc000000007d75e80 softe: 0 irq_happened: 0x01 pid = 14617, comm = bash Bad kernel stack pointer fafb4b0 at eca7cc4 cpu 0x15: Vector: 300 (Data Access) at [c000000007f07d40] pc: 000000000eca7cc4 lr: 000000000eca7c44 sp: fafb4b0 msr: 8000000000001000 dar: 10000000 dsisr: 42000000 current = 0xc0000003fafa7180 paca = 0xc000000007d75e80 softe: 0 irq_happened: 0x01 pid = 14617, comm = bash cpu 0x15: Exception 300 (Data Access) in xmon, returning to main loop xmon: WARNING: bad recursive fault on cpu 0x15 The root cause is that xmon is calling RTAS to turn off the surveillance when entering xmon, and RTAS is requiring big endian parameters. This patch is byte swapping the RTAS arguments when running in LE mode. Cc: stable@vger.kernel.org Signed-off-by: Laurent Dufour Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index b988b5a..c8efbb3 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -293,10 +293,10 @@ static inline void disable_surveillance(void) args.token = rtas_token("set-indicator"); if (args.token == RTAS_UNKNOWN_SERVICE) return; - args.nargs = 3; - args.nret = 1; + args.nargs = cpu_to_be32(3); + args.nret = cpu_to_be32(1); args.rets = &args.args[3]; - args.args[0] = SURVEILLANCE_TOKEN; + args.args[0] = cpu_to_be32(SURVEILLANCE_TOKEN); args.args[1] = 0; args.args[2] = 0; enter_rtas(__pa(&args)); -- cgit v0.10.2 From 7531473c308d133994160bf45141ced00a4f5e0f Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 25 Nov 2014 09:26:58 +1100 Subject: powerpc/eeh: Fix PE state format Obviously I had wrong format given to the PE state output from /sys/bus/pci/devices/xxxx/eeh_pe_state with some typoes, which was introduced by commit 2013add4ce73. The patch fixes it up. Fixes: 2013add4ce73 ("powerpc/eeh: Show hex prefix for PE state sysfs") Signed-off-by: Gavin Shan Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c index f19b1e5..1ceecdd 100644 --- a/arch/powerpc/kernel/eeh_sysfs.c +++ b/arch/powerpc/kernel/eeh_sysfs.c @@ -65,7 +65,7 @@ static ssize_t eeh_pe_state_show(struct device *dev, return -ENODEV; state = eeh_ops->get_state(edev->pe, NULL); - return sprintf(buf, "%0x08x %0x08x\n", + return sprintf(buf, "0x%08x 0x%08x\n", state, edev->pe->state); } -- cgit v0.10.2 From 360d88a9e3fba596a12520b242fbab1c45b983e1 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 25 Nov 2014 09:26:59 +1100 Subject: powerpc/powernv: Replace OPAL_DEASSERT_RESET with EEH_RESET_DEACTIVATE The flag passed to ioda_eeh_phb_reset() should be EEH_RESET_DEACTIVATE, which is translated to OPAL_DEASSERT_RESET or something else by the EEH backend accordingly. The patch replaces OPAL_DEASSERT_RESET with EEH_RESET_DEACTIVATE for ioda_eeh_phb_reset(). Cc: stable@vger.kernel.org Signed-off-by: Gavin Shan Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 468a0f2..bc94e39 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1997,7 +1997,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, if (is_kdump_kernel()) { pr_info(" Issue PHB reset ...\n"); ioda_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL); - ioda_eeh_phb_reset(hose, OPAL_DEASSERT_RESET); + ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE); } /* Configure M64 window */ -- cgit v0.10.2 From 152d44a853e42952f6c8a504fb1f8eefd21fd5fd Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 27 Nov 2014 08:11:28 +1100 Subject: powerpc: 32 bit getcpu VDSO function uses 64 bit instructions I used some 64 bit instructions when adding the 32 bit getcpu VDSO function. Fix it. Fixes: 18ad51dd342a ("powerpc: Add VDSO version of getcpu") Cc: stable@vger.kernel.org Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/kernel/vdso32/getcpu.S b/arch/powerpc/kernel/vdso32/getcpu.S index 23eb9a9..c62be60 100644 --- a/arch/powerpc/kernel/vdso32/getcpu.S +++ b/arch/powerpc/kernel/vdso32/getcpu.S @@ -30,8 +30,8 @@ V_FUNCTION_BEGIN(__kernel_getcpu) .cfi_startproc mfspr r5,SPRN_SPRG_VDSO_READ - cmpdi cr0,r3,0 - cmpdi cr1,r4,0 + cmpwi cr0,r3,0 + cmpwi cr1,r4,0 clrlwi r6,r5,16 rlwinm r7,r5,16,31-15,31-0 beq cr0,1f -- cgit v0.10.2 From 1348579433566355e570008929daa09a0db64fd8 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 14 Nov 2014 12:08:34 -0500 Subject: drm/radeon: report disconnected for LVDS/eDP with PX if ddc fails If ddc fails, presumably the i2c mux (and hopefully the signal mux) are switched to the other GPU so don't fetch the edid from the vbios so that the connector reports disconnected. bug: https://bugzilla.opensuse.org/show_bug.cgi?id=904417 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 300c4b3..26baa9c 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -322,6 +322,12 @@ static void radeon_connector_get_edid(struct drm_connector *connector) } if (!radeon_connector->edid) { + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + return; + if (rdev->is_atom_bios) { /* some laptops provide a hardcoded edid in rom for LCDs */ if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) || @@ -826,6 +832,8 @@ static int radeon_lvds_mode_valid(struct drm_connector *connector, static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = radeon_best_single_encoder(connector); enum drm_connector_status ret = connector_status_disconnected; @@ -842,7 +850,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; - + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; } /* check for edid as well */ @@ -1589,6 +1601,11 @@ radeon_dp_detect(struct drm_connector *connector, bool force) /* check if panel is valid */ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240) ret = connector_status_connected; + /* don't fetch the edid from the vbios if ddc fails and runpm is + * enabled so we report disconnected. + */ + if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0)) + ret = connector_status_disconnected; } /* eDP is always DP */ radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; -- cgit v0.10.2 From 69eba10e606a80665f8573221fec589430d9d1cb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 27 Nov 2014 01:34:43 +0300 Subject: ALSA: hda - using uninitialized data In olden times the snd_hda_param_read() function always set "*start_id" but in 2007 we introduced a new return and it causes uninitialized data bugs in a couple of the callers: print_codec_info() and hdmi_parse_codec(). Fixes: e8a7f136f5ed ('[ALSA] hda-intel - Improve HD-audio codec probing robustness') Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b2d5899..2fe86d2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -346,8 +346,10 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, unsigned int parm; parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) + if (parm == -1) { + *start_id = 0; return 0; + } *start_id = (parm >> 16) & 0x7fff; return (int)(parm & 0x7fff); } -- cgit v0.10.2 From 9a343b9eb8c1f8db9dae804709a6578379a538f3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 26 Nov 2014 17:55:31 +0000 Subject: ARM: tegra: irq: fix buggy usage of irq_data irq field The crazy gic_arch_extn thing that Tegra uses contains multiple references to the irq field in struct irq_data, and uses this to directly poke hardware register. But irq is the *virtual* irq number, something that has nothing to do with the actual HW irq (stored in the hwirq field). And once we put the stacked domain code in action, the whole thing explodes, as these two values are *very* different: root@bacon-fat:~# cat /proc/interrupts CPU0 CPU1 16: 25801 2075 GIC 29 twd 17: 0 0 GIC 73 timer0 112: 0 0 GPIO 58 c8000600.sdhci cd 123: 0 0 GPIO 69 c8000200.sdhci cd 279: 1126 0 GIC 122 serial 281: 0 0 GIC 70 7000c000.i2c 282: 0 0 GIC 116 7000c400.i2c 283: 0 0 GIC 124 7000c500.i2c 284: 300 0 GIC 85 7000d000.i2c [...] Just replacing all instances of irq with hwirq fixes the issue. Signed-off-by: Marc Zyngier Acked-by: Thierry Reding Signed-off-by: Arnd Bergmann diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index da7be13..ab95f53 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -99,42 +99,42 @@ static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg) static void tegra_mask(struct irq_data *d) { - if (d->irq < FIRST_LEGACY_IRQ) + if (d->hwirq < FIRST_LEGACY_IRQ) return; - tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR); + tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_CLR); } static void tegra_unmask(struct irq_data *d) { - if (d->irq < FIRST_LEGACY_IRQ) + if (d->hwirq < FIRST_LEGACY_IRQ) return; - tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET); + tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_SET); } static void tegra_ack(struct irq_data *d) { - if (d->irq < FIRST_LEGACY_IRQ) + if (d->hwirq < FIRST_LEGACY_IRQ) return; - tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); + tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR); } static void tegra_eoi(struct irq_data *d) { - if (d->irq < FIRST_LEGACY_IRQ) + if (d->hwirq < FIRST_LEGACY_IRQ) return; - tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); + tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR); } static int tegra_retrigger(struct irq_data *d) { - if (d->irq < FIRST_LEGACY_IRQ) + if (d->hwirq < FIRST_LEGACY_IRQ) return 0; - tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET); + tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_SET); return 1; } @@ -142,7 +142,7 @@ static int tegra_retrigger(struct irq_data *d) #ifdef CONFIG_PM_SLEEP static int tegra_set_wake(struct irq_data *d, unsigned int enable) { - u32 irq = d->irq; + u32 irq = d->hwirq; u32 index, mask; if (irq < FIRST_LEGACY_IRQ || -- cgit v0.10.2 From 995ab5189d1d7264e79e665dfa032a19b3ac646e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 25 Nov 2014 18:43:15 +0100 Subject: ARM: 8222/1: mvebu: enable strex backoff delay Under extremely rare conditions, in an MPCore node consisting of at least 3 CPUs, two CPUs trying to perform a STREX to data on the same shared cache line can enter a livelock situation. This patch enables the HW mechanism that overcomes the bug. This fixes the incorrect setup of the STREX backoff delay bit due to a wrong description in the specification. Note that enabling the STREX backoff delay mechanism is done by leaving the bit *cleared*, while the bit was currently being set by the proc-v7.S code. [Thomas: adapt to latest mainline, slightly reword the commit log, add stable markers.] Fixes: de4901933f6d ("arm: mm: Add support for PJ4B cpu and init routines") Cc: # v3.8+ Signed-off-by: Nadav Haklai Signed-off-by: Thomas Petazzoni Acked-by: Gregory CLEMENT Acked-by: Jason Cooper Signed-off-by: Russell King diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index b3a9478..22ac2a6 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -270,7 +270,6 @@ __v7_pj4b_setup: /* Auxiliary Debug Modes Control 1 Register */ #define PJ4B_STATIC_BP (1 << 2) /* Enable Static BP */ #define PJ4B_INTER_PARITY (1 << 8) /* Disable Internal Parity Handling */ -#define PJ4B_BCK_OFF_STREX (1 << 5) /* Enable the back off of STREX instr */ #define PJ4B_CLEAN_LINE (1 << 16) /* Disable data transfer for clean line */ /* Auxiliary Debug Modes Control 2 Register */ @@ -293,7 +292,6 @@ __v7_pj4b_setup: /* Auxiliary Debug Modes Control 1 Register */ mrc p15, 1, r0, c15, c1, 1 orr r0, r0, #PJ4B_CLEAN_LINE - orr r0, r0, #PJ4B_BCK_OFF_STREX orr r0, r0, #PJ4B_INTER_PARITY bic r0, r0, #PJ4B_STATIC_BP mcr p15, 1, r0, c15, c1, 1 -- cgit v0.10.2 From 3f4aa45ceea5789a4aade536acc27f2e0d3da5e1 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Thu, 27 Nov 2014 11:39:04 +0100 Subject: ARM: 8226/1: cacheflush: get rid of restarting block We cannot restart cacheflush safely if a process provides user-defined signal handler and signal is pending. In this case -EINTR is returned and it is expected that process re-invokes syscall. However, there are a few problems with that: * looks like nobody bothers checking return value from cacheflush * but if it did, we don't provide the restart address for that, so the process has to use the same range again * ...and again, what might lead to looping forever So, remove cacheflush restarting code and terminate cache flushing as early as fatal signal is pending. Cc: stable@vger.kernel.org # 3.12+ Reported-by: Chanho Min Signed-off-by: Vladimir Murzin Acked-by: Will Deacon Signed-off-by: Russell King diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index fc44d37..ce73ab6 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -44,16 +44,6 @@ struct cpu_context_save { __u32 extra[2]; /* Xscale 'acc' register, etc */ }; -struct arm_restart_block { - union { - /* For user cache flushing */ - struct { - unsigned long start; - unsigned long end; - } cache; - }; -}; - /* * low level task data that entry.S needs immediate access to. * __switch_to() assumes cpu_context follows immediately after cpu_domain. @@ -79,7 +69,6 @@ struct thread_info { unsigned long thumbee_state; /* ThumbEE Handler Base register */ #endif struct restart_block restart_block; - struct arm_restart_block arm_restart_block; }; #define INIT_THREAD_INFO(tsk) \ diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 0c8b108..9f5d818 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -533,8 +533,6 @@ static int bad_syscall(int n, struct pt_regs *regs) return regs->ARM_r0; } -static long do_cache_op_restart(struct restart_block *); - static inline int __do_cache_op(unsigned long start, unsigned long end) { @@ -543,24 +541,8 @@ __do_cache_op(unsigned long start, unsigned long end) do { unsigned long chunk = min(PAGE_SIZE, end - start); - if (signal_pending(current)) { - struct thread_info *ti = current_thread_info(); - - ti->restart_block = (struct restart_block) { - .fn = do_cache_op_restart, - }; - - ti->arm_restart_block = (struct arm_restart_block) { - { - .cache = { - .start = start, - .end = end, - }, - }, - }; - - return -ERESTART_RESTARTBLOCK; - } + if (fatal_signal_pending(current)) + return 0; ret = flush_cache_user_range(start, start + chunk); if (ret) @@ -573,15 +555,6 @@ __do_cache_op(unsigned long start, unsigned long end) return 0; } -static long do_cache_op_restart(struct restart_block *unused) -{ - struct arm_restart_block *restart_block; - - restart_block = ¤t_thread_info()->arm_restart_block; - return __do_cache_op(restart_block->cache.start, - restart_block->cache.end); -} - static inline int do_cache_op(unsigned long start, unsigned long end, int flags) { -- cgit v0.10.2 From 070e76cb3ffe43f6855492e77c96680c562598f0 Mon Sep 17 00:00:00 2001 From: Leonid Yegoshin Date: Thu, 27 Nov 2014 11:13:08 +0000 Subject: MIPS: tlbex: Fix potential HTW race on TLBL/M/S handlers There is a potential race when probing the TLB in TLBL/M/S exception handlers for a matching entry. Between the time we hit a TLBL/S/M exception and the time we get to execute the TLBP instruction, the HTW may have replaced the TLB entry we are interested in hence the TLB probe may fail. However, in the existing handlers, we never checked the status of the TLBP (ie check the result in the C0/Index register). We fix this by adding such a check when the core implements the HTW. If we couldn't find a matching entry, we return back and try again. Signed-off-by: Leonid Yegoshin Signed-off-by: Markos Chandras Reviewed-by: James Hogan Cc: # v3.17+ Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8599/ Signed-off-by: Ralf Baechle diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index b5f228e..e3328a9 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -1872,8 +1872,16 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, uasm_l_smp_pgtable_change(l, *p); #endif iPTE_LW(p, wr.r1, wr.r2); /* get even pte */ - if (!m4kc_tlbp_war()) + if (!m4kc_tlbp_war()) { build_tlb_probe_entry(p); + if (cpu_has_htw) { + /* race condition happens, leaving */ + uasm_i_ehb(p); + uasm_i_mfc0(p, wr.r3, C0_INDEX); + uasm_il_bltz(p, r, wr.r3, label_leave); + uasm_i_nop(p); + } + } return wr; } -- cgit v0.10.2 From 6d4556fc0309608f760f1d329df56d77fdd0c31a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Nov 2014 10:10:21 -0600 Subject: staging: r8188eu: Add new device ID for DLink GO-USB-N150 The DLink GO-USB-N150 with revision B1 uses this driver. Signed-off-by: Larry Finger Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 407a318..2f87150 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -47,6 +47,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */ {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */ + {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */ {} /* Terminating entry */ }; -- cgit v0.10.2 From 37e661ee10c6d0d1310c62b3d29ae9a63073ac5d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Nov 2014 11:28:07 +0100 Subject: ALSA: hda - Add AZX_DCAPS_SNOOP_OFF (and refactor snoop setup) Add a new driver_caps bit, AZX_DCAPS_SNOOP_OFF, to set the snoop off as default. This new bit is used for the checks in azx_check_snoop_available(). Most of case-switches are replaced with the new dcaps in each entry. While working on it, for avoiding to spend more bits, combine three bits AZX_DCAPS_SNOOP_SCH, AZX_DCAPS_SNOOP_ATI and AZX_DCAPS_SNOOP_NVIDIA bits into a flat type of two bits. This reduces the bits usages, and assign AZX_DCAPS_OFF to this empty bit now. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 966e6f9..633020d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -272,42 +272,51 @@ enum { AZX_NUM_DRIVERS, /* keep this as last entry */ }; +#define azx_get_snoop_type(chip) \ + (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10) +#define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10) + /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE | \ - AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_REVERSE_ASSIGN) + (AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ + AZX_DCAPS_REVERSE_ASSIGN | AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) #define AZX_DCAPS_INTEL_HASWELL \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ - AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \ - AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ + AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ - AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_PM_RUNTIME | \ - AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ + AZX_DCAPS_SNOOP_TYPE(SCH)) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ - (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ - AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) + (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_SNOOP_TYPE(ATI)) /* quirks for ATI/AMD HDMI */ #define AZX_DCAPS_PRESET_ATI_HDMI \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) +/* quirks for ATI HDMI with snoop off */ +#define AZX_DCAPS_PRESET_ATI_HDMI_NS \ + (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF) + /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ - AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_NO_64BIT |\ - AZX_DCAPS_CORBRP_SELF_CLEAR) + (AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | AZX_DCAPS_ALIGN_BUFSIZE |\ + AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\ + AZX_DCAPS_SNOOP_TYPE(NVIDIA)) #define AZX_DCAPS_PRESET_CTHDA \ - (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY) + (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF) /* * VGA-switcher support @@ -436,6 +445,8 @@ static void update_pci_byte(struct pci_dev *pci, unsigned int reg, static void azx_init_pci(struct azx *chip) { + int snoop_type = azx_get_snoop_type(chip); + /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) * TCSEL == Traffic Class Select Register, which sets PCI express QOS * Ensuring these bits are 0 clears playback static on some HD Audio @@ -450,7 +461,7 @@ static void azx_init_pci(struct azx *chip) /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio, * we need to enable snoop. */ - if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_ATI) { dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n", azx_snoop(chip)); update_pci_byte(chip->pci, @@ -459,7 +470,7 @@ static void azx_init_pci(struct azx *chip) } /* For NVIDIA HDA, enable snoop */ - if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_NVIDIA) { dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n", azx_snoop(chip)); update_pci_byte(chip->pci, @@ -474,7 +485,7 @@ static void azx_init_pci(struct azx *chip) } /* Enable SCH/PCH snoop if needed */ - if (chip->driver_caps & AZX_DCAPS_SCH_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_SCH) { unsigned short snoop; pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) || @@ -1361,8 +1372,8 @@ static void azx_check_snoop_available(struct azx *chip) { bool snoop = chip->snoop; - switch (chip->driver_type) { - case AZX_DRIVER_VIA: + if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE && + chip->driver_type == AZX_DRIVER_VIA) { /* force to non-snoop mode for a new VIA controller * when BIOS is set */ @@ -1372,17 +1383,11 @@ static void azx_check_snoop_available(struct azx *chip) if (!(val & 0x80) && chip->pci->revision == 0x30) snoop = false; } - break; - case AZX_DRIVER_ATIHDMI_NS: - /* new ATI HDMI requires non-snoop */ - snoop = false; - break; - case AZX_DRIVER_CTHDA: - case AZX_DRIVER_CMEDIA: - snoop = false; - break; } + if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF) + snoop = false; + if (snoop != chip->snoop) { dev_info(chip->card->dev, "Force to %s mode\n", snoop ? "snoop" : "non-snoop"); @@ -2116,13 +2121,13 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1002, 0xaa98), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0x9902), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaaa0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaaa8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaab0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, /* VIA VT8251/VT8237A */ { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA }, @@ -2169,7 +2174,7 @@ static const struct pci_device_id azx_ids[] = { /* CM8888 */ { PCI_DEVICE(0x13f6, 0x5011), .driver_data = AZX_DRIVER_CMEDIA | - AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB }, + AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF }, /* Vortex86MX */ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* VMware HDAudio */ diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index 949cd43..602536c 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -152,9 +152,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* bits 0-7 are used for indicating driver type */ #define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ #define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_ATI_SNOOP (1 << 10) /* ATI snoop enable */ -#define AZX_DCAPS_NVIDIA_SNOOP (1 << 11) /* Nvidia snoop enable */ -#define AZX_DCAPS_SCH_SNOOP (1 << 12) /* SCH/PCH snoop enable */ +#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ +#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ #define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ #define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ @@ -172,6 +171,13 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ +enum { + AZX_SNOOP_TYPE_NONE , + AZX_SNOOP_TYPE_SCH, + AZX_SNOOP_TYPE_ATI, + AZX_SNOOP_TYPE_NVIDIA, +}; + /* HD Audio class code */ #define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 -- cgit v0.10.2 From 7c7320157a37ed459b59e2f6b53b73780b12ad80 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 25 Nov 2014 12:54:16 +0100 Subject: ALSA: hda - Allow forcibly enabling/disabling snoop User can pass snoop option to enable/disable the snoop behavior, but currently azx_check_snoop_available() always turns it off for some devices. For better debuggability, change the parameter as bint, and allow user to enable/disable forcibly the snoop when specified via the module option. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 633020d..728663d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -196,8 +196,8 @@ MODULE_PARM_DESC(align_buffer_size, "Force buffer and period sizes to be multiple of 128 bytes."); #ifdef CONFIG_X86 -static bool hda_snoop = true; -module_param_named(snoop, hda_snoop, bool, 0444); +static int hda_snoop = -1; +module_param_named(snoop, hda_snoop, bint, 0444); MODULE_PARM_DESC(snoop, "Enable/disable snooping"); #else #define hda_snoop true @@ -1370,29 +1370,33 @@ static void check_msi(struct azx *chip) /* check the snoop mode availability */ static void azx_check_snoop_available(struct azx *chip) { - bool snoop = chip->snoop; + int snoop = hda_snoop; + if (snoop >= 0) { + dev_info(chip->card->dev, "Force to %s mode by module option\n", + snoop ? "snoop" : "non-snoop"); + chip->snoop = snoop; + return; + } + + snoop = true; if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE && chip->driver_type == AZX_DRIVER_VIA) { /* force to non-snoop mode for a new VIA controller * when BIOS is set */ - if (snoop) { - u8 val; - pci_read_config_byte(chip->pci, 0x42, &val); - if (!(val & 0x80) && chip->pci->revision == 0x30) - snoop = false; - } + u8 val; + pci_read_config_byte(chip->pci, 0x42, &val); + if (!(val & 0x80) && chip->pci->revision == 0x30) + snoop = false; } if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF) snoop = false; - if (snoop != chip->snoop) { - dev_info(chip->card->dev, "Force to %s mode\n", - snoop ? "snoop" : "non-snoop"); - chip->snoop = snoop; - } + chip->snoop = snoop; + if (!snoop) + dev_info(chip->card->dev, "Force to non-snoop mode\n"); } static void azx_probe_work(struct work_struct *work) @@ -1452,7 +1456,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, check_probe_mask(chip, dev); chip->single_cmd = single_cmd; - chip->snoop = hda_snoop; azx_check_snoop_available(chip); if (bdl_pos_adj[dev] < 0) { -- cgit v0.10.2 From 7a2e9ddc903225d8fb3a510a842144a239017ee4 Mon Sep 17 00:00:00 2001 From: Jurgen Kramer Date: Fri, 28 Nov 2014 17:32:53 +0100 Subject: ALSA: usb-audio: Add native DSD support for Denon/Marantz DACs This patch adds native DSD support for the following devices: - Marantz SA-14S1 - Marants HD-DAC1 Signed-off-by: Jurgen Kramer Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2c1018e..a9d4add 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1213,5 +1213,16 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, break; } + /* Denon/Marantz devices with USB DAC functionality */ + switch (chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + if (fp->altsetting == 2) + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + break; + default: + break; + } + return 0; } -- cgit v0.10.2 From 6874daad4b0fbed5b2f9bef7f4d3f2b895463a95 Mon Sep 17 00:00:00 2001 From: Jurgen Kramer Date: Fri, 28 Nov 2014 17:32:54 +0100 Subject: ALSA: usb-audio: Add mode select quirk for Denon/Marantz DACs Denon/Marantz USB DACs need a specific vendor command to switch between PCM and DSD mode. This patch adds a new quirk function to switch between the two modes using the specific USB vendor command. This patch applies to the following devices: - Marantz SA-14S1 - Marantz HD-DAC1 Signed-off-by: Jurgen Kramer Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c62a165..0d8aba5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -482,6 +482,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* set interface */ if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) { + + err = snd_usb_select_mode_quirk(subs, fmt); + if (err < 0) + return -EIO; + err = usb_set_interface(dev, fmt->iface, fmt->altsetting); if (err < 0) { dev_err(&dev->dev, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a9d4add..e0cde74 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1111,6 +1111,44 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, } } + +/* Marantz/Denon USB DACs need a vendor cmd to switch + * between PCM and native DSD mode + */ +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + int err; + + switch (subs->stream->chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + + /* First switch to alt set 0, otherwise the mode switch cmd + * will not be accepted by the DAC + */ + err = usb_set_interface(dev, fmt->iface, 0); + if (err < 0) + return err; + + mdelay(20); /* Delay needed after setting the interface */ + + switch (fmt->altsetting) { + case 2: /* DSD mode requested */ + case 1: /* PCM mode requested */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + fmt->altsetting - 1, 1, NULL, 0); + if (err < 0) + return err; + break; + } + mdelay(20); + } + return 0; +} + void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) { /* diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 665e972..1b86238 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -31,6 +31,9 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt); + u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int sample_bytes); -- cgit v0.10.2 From 81fc5ad51539e765cd470f1d0d9d0477d1c4c30c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:10 +0900 Subject: ALSA: dice: suppress checkpatch.pl warnings The checkpatch.pl generates some warnings due to: - C99 comment - a line over 80 characters - min() for parameters with different types Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index e3a04d6..72af110 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -133,7 +133,7 @@ static inline u64 global_address(struct dice *dice, unsigned int offset) return DICE_PRIVATE_SPACE + dice->global_offset + offset; } -// TODO: rx index +/* TODO: rx index */ static inline u64 rx_address(struct dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->rx_offset + offset; @@ -721,13 +721,14 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, event.lock_status.status = dice->dev_lock_count > 0; dice->dev_lock_changed = false; - count = min(count, (long)sizeof(event.lock_status)); + count = min_t(long, count, sizeof(event.lock_status)); } else { - event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; event.dice_notification.notification = dice->notification_bits; dice->notification_bits = 0; - count = min(count, (long)sizeof(event.dice_notification)); + count = min_t(long, count, sizeof(event.dice_notification)); } spin_unlock_irq(&dice->lock); -- cgit v0.10.2 From 732d153fbeac1774393508b3cec4b1d09e567585 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:11 +0900 Subject: ALSA: dice: Rename structure and its members Currently, dice driver supports AMDTP out-stream. In followed commits, AMDTP in-stream will be supported but current name of members in dice structure are not propper. This commit renames these members to proper name. Additionally, for easily distinguishing local symbols from structure tag, rename dice tag into snd_dice. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 72af110..eafd74b 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -32,7 +32,7 @@ #include "dice-interface.h" -struct dice { +struct snd_dice { struct snd_card *card; struct fw_unit *unit; spinlock_t lock; @@ -50,8 +50,8 @@ struct dice { struct completion clock_accepted; wait_queue_head_t hwdep_wait; u32 notification_bits; - struct fw_iso_resources resources; - struct amdtp_stream stream; + struct fw_iso_resources rx_resources; + struct amdtp_stream rx_stream; }; MODULE_DESCRIPTION("DICE driver"); @@ -87,13 +87,13 @@ static unsigned int rate_index_to_mode(unsigned int rate_index) return ((int)rate_index - 1) / 2; } -static void dice_lock_changed(struct dice *dice) +static void dice_lock_changed(struct snd_dice *dice) { dice->dev_lock_changed = true; wake_up(&dice->hwdep_wait); } -static int dice_try_lock(struct dice *dice) +static int dice_try_lock(struct snd_dice *dice) { int err; @@ -114,7 +114,7 @@ out: return err; } -static void dice_unlock(struct dice *dice) +static void dice_unlock(struct snd_dice *dice) { spin_lock_irq(&dice->lock); @@ -128,18 +128,18 @@ out: spin_unlock_irq(&dice->lock); } -static inline u64 global_address(struct dice *dice, unsigned int offset) +static inline u64 global_address(struct snd_dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; } /* TODO: rx index */ -static inline u64 rx_address(struct dice *dice, unsigned int offset) +static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->rx_offset + offset; } -static int dice_owner_set(struct dice *dice) +static int dice_owner_set(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -183,7 +183,7 @@ static int dice_owner_set(struct dice *dice) return err; } -static int dice_owner_update(struct dice *dice) +static int dice_owner_update(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -226,7 +226,7 @@ static int dice_owner_update(struct dice *dice) return err; } -static void dice_owner_clear(struct dice *dice) +static void dice_owner_clear(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -249,7 +249,7 @@ static void dice_owner_clear(struct dice *dice) dice->owner_generation = -1; } -static int dice_enable_set(struct dice *dice) +static int dice_enable_set(struct snd_dice *dice) { __be32 value; int err; @@ -267,7 +267,7 @@ static int dice_enable_set(struct dice *dice) return 0; } -static void dice_enable_clear(struct dice *dice) +static void dice_enable_clear(struct snd_dice *dice) { __be32 value; @@ -288,7 +288,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, int generation, unsigned long long offset, void *data, size_t length, void *callback_data) { - struct dice *dice = callback_data; + struct snd_dice *dice = callback_data; u32 bits; unsigned long flags; @@ -317,7 +317,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct dice *dice = rule->private; + struct snd_dice *dice = rule->private; const struct snd_interval *channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *rate = @@ -344,7 +344,7 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct dice *dice = rule->private; + struct snd_dice *dice = rule->private; const struct snd_interval *rate = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = @@ -384,7 +384,7 @@ static int dice_open(struct snd_pcm_substream *substream) .periods_min = 1, .periods_max = UINT_MAX, }; - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; unsigned int i; int err; @@ -420,7 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream) if (err < 0) goto err_lock; - err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime); + err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); if (err < 0) goto err_lock; @@ -434,47 +434,47 @@ error: static int dice_close(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; dice_unlock(dice); return 0; } -static int dice_stream_start_packets(struct dice *dice) +static int dice_stream_start_packets(struct snd_dice *dice) { int err; - if (amdtp_stream_running(&dice->stream)) + if (amdtp_stream_running(&dice->rx_stream)) return 0; - err = amdtp_stream_start(&dice->stream, dice->resources.channel, + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, fw_parent_device(dice->unit)->max_speed); if (err < 0) return err; err = dice_enable_set(dice); if (err < 0) { - amdtp_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->rx_stream); return err; } return 0; } -static int dice_stream_start(struct dice *dice) +static int dice_stream_start(struct snd_dice *dice) { __be32 channel; int err; - if (!dice->resources.allocated) { - err = fw_iso_resources_allocate(&dice->resources, - amdtp_stream_get_max_payload(&dice->stream), + if (!dice->rx_resources.allocated) { + err = fw_iso_resources_allocate(&dice->rx_resources, + amdtp_stream_get_max_payload(&dice->rx_stream), fw_parent_device(dice->unit)->max_speed); if (err < 0) goto error; - channel = cpu_to_be32(dice->resources.channel); + channel = cpu_to_be32(dice->rx_resources.channel); err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), @@ -494,36 +494,36 @@ err_rx_channel: snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); err_resources: - fw_iso_resources_free(&dice->resources); + fw_iso_resources_free(&dice->rx_resources); error: return err; } -static void dice_stream_stop_packets(struct dice *dice) +static void dice_stream_stop_packets(struct snd_dice *dice) { - if (amdtp_stream_running(&dice->stream)) { + if (amdtp_stream_running(&dice->rx_stream)) { dice_enable_clear(dice); - amdtp_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->rx_stream); } } -static void dice_stream_stop(struct dice *dice) +static void dice_stream_stop(struct snd_dice *dice) { __be32 channel; dice_stream_stop_packets(dice); - if (!dice->resources.allocated) + if (!dice->rx_resources.allocated) return; channel = cpu_to_be32((u32)-1); snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); - fw_iso_resources_free(&dice->resources); + fw_iso_resources_free(&dice->rx_resources); } -static int dice_change_rate(struct dice *dice, unsigned int clock_rate) +static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) { __be32 value; int err; @@ -547,7 +547,7 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate) static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; unsigned int rate_index, mode, rate, channels, i; int err; @@ -585,24 +585,24 @@ static int dice_hw_params(struct snd_pcm_substream *substream, rate /= 2; channels *= 2; - dice->stream.double_pcm_frames = true; + dice->rx_stream.double_pcm_frames = true; } else { - dice->stream.double_pcm_frames = false; + dice->rx_stream.double_pcm_frames = false; } mode = rate_index_to_mode(rate_index); - amdtp_stream_set_parameters(&dice->stream, rate, channels, + amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, dice->rx_midi_ports[mode]); if (rate_index > 4) { channels /= 2; for (i = 0; i < channels; i++) { - dice->stream.pcm_positions[i] = i * 2; - dice->stream.pcm_positions[i + channels] = i * 2 + 1; + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; } } - amdtp_stream_set_pcm_format(&dice->stream, + amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return 0; @@ -610,7 +610,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, static int dice_hw_free(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); dice_stream_stop(dice); @@ -621,12 +621,12 @@ static int dice_hw_free(struct snd_pcm_substream *substream) static int dice_prepare(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; int err; mutex_lock(&dice->mutex); - if (amdtp_streaming_error(&dice->stream)) + if (amdtp_streaming_error(&dice->rx_stream)) dice_stream_stop_packets(dice); err = dice_stream_start(dice); @@ -637,14 +637,14 @@ static int dice_prepare(struct snd_pcm_substream *substream) mutex_unlock(&dice->mutex); - amdtp_stream_pcm_prepare(&dice->stream); + amdtp_stream_pcm_prepare(&dice->rx_stream); return 0; } static int dice_trigger(struct snd_pcm_substream *substream, int cmd) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { @@ -657,19 +657,19 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_stream_pcm_trigger(&dice->stream, pcm); + amdtp_stream_pcm_trigger(&dice->rx_stream, pcm); return 0; } static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; - return amdtp_stream_pcm_pointer(&dice->stream); + return amdtp_stream_pcm_pointer(&dice->rx_stream); } -static int dice_create_pcm(struct dice *dice) +static int dice_create_pcm(struct snd_dice *dice) { static struct snd_pcm_ops ops = { .open = dice_open, @@ -699,7 +699,7 @@ static int dice_create_pcm(struct dice *dice) static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; DEFINE_WAIT(wait); union snd_firewire_event event; @@ -742,7 +742,7 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; unsigned int events; poll_wait(file, &dice->hwdep_wait, wait); @@ -757,7 +757,7 @@ static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, return events; } -static int dice_hwdep_get_info(struct dice *dice, void __user *arg) +static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) { struct fw_device *dev = fw_parent_device(dice->unit); struct snd_firewire_get_info info; @@ -776,7 +776,7 @@ static int dice_hwdep_get_info(struct dice *dice, void __user *arg) return 0; } -static int dice_hwdep_lock(struct dice *dice) +static int dice_hwdep_lock(struct snd_dice *dice) { int err; @@ -794,7 +794,7 @@ static int dice_hwdep_lock(struct dice *dice) return err; } -static int dice_hwdep_unlock(struct dice *dice) +static int dice_hwdep_unlock(struct snd_dice *dice) { int err; @@ -814,7 +814,7 @@ static int dice_hwdep_unlock(struct dice *dice) static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; spin_lock_irq(&dice->lock); if (dice->dev_lock_count == -1) @@ -827,7 +827,7 @@ static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; switch (cmd) { case SNDRV_FIREWIRE_IOCTL_GET_INFO: @@ -852,7 +852,7 @@ static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define dice_hwdep_compat_ioctl NULL #endif -static int dice_create_hwdep(struct dice *dice) +static int dice_create_hwdep(struct snd_dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, @@ -876,7 +876,7 @@ static int dice_create_hwdep(struct dice *dice) return 0; } -static int dice_proc_read_mem(struct dice *dice, void *buffer, +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, unsigned int offset_q, unsigned int quadlets) { unsigned int i; @@ -899,8 +899,8 @@ static const char *str_from_array(const char *const strs[], unsigned int count, { if (i < count) return strs[i]; - else - return "(unknown)"; + + return "(unknown)"; } static void dice_proc_fixup_string(char *s, unsigned int size) @@ -935,7 +935,7 @@ static void dice_proc_read(struct snd_info_entry *entry, "32000", "44100", "48000", "88200", "96000", "176400", "192000", "any low", "any mid", "any high", "none" }; - struct dice *dice = entry->private_data; + struct snd_dice *dice = entry->private_data; u32 sections[ARRAY_SIZE(section_names) * 2]; struct { u32 number; @@ -1110,7 +1110,7 @@ static void dice_proc_read(struct snd_info_entry *entry, } } -static void dice_create_proc(struct dice *dice) +static void dice_create_proc(struct snd_dice *dice) { struct snd_info_entry *entry; @@ -1120,9 +1120,9 @@ static void dice_create_proc(struct dice *dice) static void dice_card_free(struct snd_card *card) { - struct dice *dice = card->private_data; + struct snd_dice *dice = card->private_data; - amdtp_stream_destroy(&dice->stream); + amdtp_stream_destroy(&dice->rx_stream); fw_core_remove_address_handler(&dice->notification_handler); mutex_destroy(&dice->mutex); } @@ -1217,7 +1217,7 @@ static int dice_interface_check(struct fw_unit *unit) return 0; } -static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) +static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) { int i; @@ -1229,7 +1229,7 @@ static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) return -1; } -static int dice_read_mode_params(struct dice *dice, unsigned int mode) +static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) { __be32 values[2]; int rate_index, err; @@ -1257,7 +1257,7 @@ static int dice_read_mode_params(struct dice *dice, unsigned int mode) return 0; } -static int dice_read_params(struct dice *dice) +static int dice_read_params(struct snd_dice *dice) { __be32 pointers[6]; __be32 value; @@ -1298,7 +1298,7 @@ static int dice_read_params(struct dice *dice) return 0; } -static void dice_card_strings(struct dice *dice) +static void dice_card_strings(struct snd_dice *dice) { struct snd_card *card = dice->card; struct fw_device *dev = fw_parent_device(dice->unit); @@ -1336,7 +1336,7 @@ static void dice_card_strings(struct dice *dice) static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_card *card; - struct dice *dice; + struct snd_dice *dice; __be32 clock_sel; int err; @@ -1373,12 +1373,12 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto err_owner; - err = fw_iso_resources_init(&dice->resources, unit); + err = fw_iso_resources_init(&dice->rx_resources, unit); if (err < 0) goto err_owner; - dice->resources.channels_mask = 0x00000000ffffffffuLL; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM, + err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, CIP_BLOCKING); if (err < 0) goto err_resources; @@ -1419,7 +1419,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) return 0; err_resources: - fw_iso_resources_destroy(&dice->resources); + fw_iso_resources_destroy(&dice->rx_resources); err_owner: dice_owner_clear(dice); err_notification_handler: @@ -1433,9 +1433,9 @@ error: static void dice_remove(struct fw_unit *unit) { - struct dice *dice = dev_get_drvdata(&unit->device); + struct snd_dice *dice = dev_get_drvdata(&unit->device); - amdtp_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->rx_stream); snd_card_disconnect(dice->card); @@ -1451,7 +1451,7 @@ static void dice_remove(struct fw_unit *unit) static void dice_bus_reset(struct fw_unit *unit) { - struct dice *dice = dev_get_drvdata(&unit->device); + struct snd_dice *dice = dev_get_drvdata(&unit->device); /* * On a bus reset, the DICE firmware disables streaming and then goes @@ -1461,7 +1461,7 @@ static void dice_bus_reset(struct fw_unit *unit) * to stop so that the application can restart them in an orderly * manner. */ - amdtp_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->rx_stream); mutex_lock(&dice->mutex); @@ -1470,7 +1470,7 @@ static void dice_bus_reset(struct fw_unit *unit) dice_owner_update(dice); - fw_iso_resources_update(&dice->resources); + fw_iso_resources_update(&dice->rx_resources); mutex_unlock(&dice->mutex); } -- cgit v0.10.2 From 14ff6a094815988b018ea4d698c2e2cc3ceee27c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:12 +0900 Subject: ALSA: dice: Move file to its own directory In followed commits, dice driver is split into several files. For easily managing these files, this commit adds subdirectory and move file into the directory. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index fad8d49..c50761c 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,12 +1,11 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o -snd-dice-objs := dice.o snd-firewire-speakers-objs := speakers.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o -obj-$(CONFIG_SND_DICE) += snd-dice.o +obj-$(CONFIG_SND_DICE) += dice/ obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h deleted file mode 100644 index 27b044f..0000000 --- a/sound/firewire/dice-interface.h +++ /dev/null @@ -1,371 +0,0 @@ -#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED -#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED - -/* - * DICE device interface definitions - */ - -/* - * Generally, all registers can be read like memory, i.e., with quadlet read or - * block read transactions with at least quadlet-aligned offset and length. - * Writes are not allowed except where noted; quadlet-sized registers must be - * written with a quadlet write transaction. - * - * All values are in big endian. The DICE firmware runs on a little-endian CPU - * and just byte-swaps _all_ quadlets on the bus, so values without endianness - * (e.g. strings) get scrambled and must be byte-swapped again by the driver. - */ - -/* - * Streaming is handled by the "DICE driver" interface. Its registers are - * located in this private address space. - */ -#define DICE_PRIVATE_SPACE 0xffffe0000000uLL - -/* - * The registers are organized in several sections, which are organized - * separately to allow them to be extended individually. Whether a register is - * supported can be detected by checking its offset against its section's size. - * - * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/ - * size values are measured in quadlets. Read-only. - */ -#define DICE_GLOBAL_OFFSET 0x00 -#define DICE_GLOBAL_SIZE 0x04 -#define DICE_TX_OFFSET 0x08 -#define DICE_TX_SIZE 0x0c -#define DICE_RX_OFFSET 0x10 -#define DICE_RX_SIZE 0x14 -#define DICE_EXT_SYNC_OFFSET 0x18 -#define DICE_EXT_SYNC_SIZE 0x1c -#define DICE_UNUSED2_OFFSET 0x20 -#define DICE_UNUSED2_SIZE 0x24 - -/* - * Global settings. - */ - -/* - * Stores the full 64-bit address (node ID and offset in the node's address - * space) where the device will send notifications. Must be changed with - * a compare/swap transaction by the owner. This register is automatically - * cleared on a bus reset. - */ -#define GLOBAL_OWNER 0x000 -#define OWNER_NO_OWNER 0xffff000000000000uLL -#define OWNER_NODE_SHIFT 48 - -/* - * A bitmask with asynchronous events; read-only. When any event(s) happen, - * the bits of previous events are cleared, and the value of this register is - * also written to the address stored in the owner register. - */ -#define GLOBAL_NOTIFICATION 0x008 -/* Some registers in the Rx/Tx sections may have changed. */ -#define NOTIFY_RX_CFG_CHG 0x00000001 -#define NOTIFY_TX_CFG_CHG 0x00000002 -/* Lock status of the current clock source may have changed. */ -#define NOTIFY_LOCK_CHG 0x00000010 -/* Write to the clock select register has been finished. */ -#define NOTIFY_CLOCK_ACCEPTED 0x00000020 -/* Lock status of some clock source has changed. */ -#define NOTIFY_EXT_STATUS 0x00000040 -/* Other bits may be used for device-specific events. */ - -/* - * A name that can be customized for each device; read/write. Padded with zero - * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver - * happens to be using. - */ -#define GLOBAL_NICK_NAME 0x00c -#define NICK_NAME_SIZE 64 - -/* - * The current sample rate and clock source; read/write. Whether a clock - * source or sample rate is supported is device-specific; the internal clock - * source is always available. Low/mid/high = up to 48/96/192 kHz. This - * register can be changed even while streams are running. - */ -#define GLOBAL_CLOCK_SELECT 0x04c -#define CLOCK_SOURCE_MASK 0x000000ff -#define CLOCK_SOURCE_AES1 0x00000000 -#define CLOCK_SOURCE_AES2 0x00000001 -#define CLOCK_SOURCE_AES3 0x00000002 -#define CLOCK_SOURCE_AES4 0x00000003 -#define CLOCK_SOURCE_AES_ANY 0x00000004 -#define CLOCK_SOURCE_ADAT 0x00000005 -#define CLOCK_SOURCE_TDIF 0x00000006 -#define CLOCK_SOURCE_WC 0x00000007 -#define CLOCK_SOURCE_ARX1 0x00000008 -#define CLOCK_SOURCE_ARX2 0x00000009 -#define CLOCK_SOURCE_ARX3 0x0000000a -#define CLOCK_SOURCE_ARX4 0x0000000b -#define CLOCK_SOURCE_INTERNAL 0x0000000c -#define CLOCK_RATE_MASK 0x0000ff00 -#define CLOCK_RATE_32000 0x00000000 -#define CLOCK_RATE_44100 0x00000100 -#define CLOCK_RATE_48000 0x00000200 -#define CLOCK_RATE_88200 0x00000300 -#define CLOCK_RATE_96000 0x00000400 -#define CLOCK_RATE_176400 0x00000500 -#define CLOCK_RATE_192000 0x00000600 -#define CLOCK_RATE_ANY_LOW 0x00000700 -#define CLOCK_RATE_ANY_MID 0x00000800 -#define CLOCK_RATE_ANY_HIGH 0x00000900 -#define CLOCK_RATE_NONE 0x00000a00 -#define CLOCK_RATE_SHIFT 8 - -/* - * Enable streaming; read/write. Writing a non-zero value (re)starts all - * streams that have a valid iso channel set; zero stops all streams. The - * streams' parameters must be configured before starting. This register is - * automatically cleared on a bus reset. - */ -#define GLOBAL_ENABLE 0x050 - -/* - * Status of the sample clock; read-only. - */ -#define GLOBAL_STATUS 0x054 -/* The current clock source is locked. */ -#define STATUS_SOURCE_LOCKED 0x00000001 -/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */ -#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 - -/* - * Status of all clock sources; read-only. - */ -#define GLOBAL_EXTENDED_STATUS 0x058 -/* - * The _LOCKED bits always show the current status; any change generates - * a notification. - */ -#define EXT_STATUS_AES1_LOCKED 0x00000001 -#define EXT_STATUS_AES2_LOCKED 0x00000002 -#define EXT_STATUS_AES3_LOCKED 0x00000004 -#define EXT_STATUS_AES4_LOCKED 0x00000008 -#define EXT_STATUS_ADAT_LOCKED 0x00000010 -#define EXT_STATUS_TDIF_LOCKED 0x00000020 -#define EXT_STATUS_ARX1_LOCKED 0x00000040 -#define EXT_STATUS_ARX2_LOCKED 0x00000080 -#define EXT_STATUS_ARX3_LOCKED 0x00000100 -#define EXT_STATUS_ARX4_LOCKED 0x00000200 -#define EXT_STATUS_WC_LOCKED 0x00000400 -/* - * The _SLIP bits do not generate notifications; a set bit indicates that an - * error occurred since the last time when this register was read with - * a quadlet read transaction. - */ -#define EXT_STATUS_AES1_SLIP 0x00010000 -#define EXT_STATUS_AES2_SLIP 0x00020000 -#define EXT_STATUS_AES3_SLIP 0x00040000 -#define EXT_STATUS_AES4_SLIP 0x00080000 -#define EXT_STATUS_ADAT_SLIP 0x00100000 -#define EXT_STATUS_TDIF_SLIP 0x00200000 -#define EXT_STATUS_ARX1_SLIP 0x00400000 -#define EXT_STATUS_ARX2_SLIP 0x00800000 -#define EXT_STATUS_ARX3_SLIP 0x01000000 -#define EXT_STATUS_ARX4_SLIP 0x02000000 -#define EXT_STATUS_WC_SLIP 0x04000000 - -/* - * The measured rate of the current clock source, in Hz; read-only. - */ -#define GLOBAL_SAMPLE_RATE 0x05c - -/* - * The version of the DICE driver specification that this device conforms to; - * read-only. - */ -#define GLOBAL_VERSION 0x060 - -/* Some old firmware versions do not have the following global registers: */ - -/* - * Supported sample rates and clock sources; read-only. - */ -#define GLOBAL_CLOCK_CAPABILITIES 0x064 -#define CLOCK_CAP_RATE_32000 0x00000001 -#define CLOCK_CAP_RATE_44100 0x00000002 -#define CLOCK_CAP_RATE_48000 0x00000004 -#define CLOCK_CAP_RATE_88200 0x00000008 -#define CLOCK_CAP_RATE_96000 0x00000010 -#define CLOCK_CAP_RATE_176400 0x00000020 -#define CLOCK_CAP_RATE_192000 0x00000040 -#define CLOCK_CAP_SOURCE_AES1 0x00010000 -#define CLOCK_CAP_SOURCE_AES2 0x00020000 -#define CLOCK_CAP_SOURCE_AES3 0x00040000 -#define CLOCK_CAP_SOURCE_AES4 0x00080000 -#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 -#define CLOCK_CAP_SOURCE_ADAT 0x00200000 -#define CLOCK_CAP_SOURCE_TDIF 0x00400000 -#define CLOCK_CAP_SOURCE_WC 0x00800000 -#define CLOCK_CAP_SOURCE_ARX1 0x01000000 -#define CLOCK_CAP_SOURCE_ARX2 0x02000000 -#define CLOCK_CAP_SOURCE_ARX3 0x04000000 -#define CLOCK_CAP_SOURCE_ARX4 0x08000000 -#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 - -/* - * Names of all clock sources; read-only. Quadlets are byte-swapped. Names - * are separated with one backslash, the list is terminated with two - * backslashes. Unused clock sources are included. - */ -#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 -#define CLOCK_SOURCE_NAMES_SIZE 256 - -/* - * Capture stream settings. This section includes the number/size registers - * and the registers of all streams. - */ - -/* - * The number of supported capture streams; read-only. - */ -#define TX_NUMBER 0x000 - -/* - * The size of one stream's register block, in quadlets; read-only. The - * registers of the first stream follow immediately afterwards; the registers - * of the following streams are offset by this register's value. - */ -#define TX_SIZE 0x004 - -/* - * The isochronous channel number on which packets are sent, or -1 if the - * stream is not to be used; read/write. - */ -#define TX_ISOCHRONOUS 0x008 - -/* - * The number of audio channels; read-only. There will be one quadlet per - * channel; the first channel is the first quadlet in a data block. - */ -#define TX_NUMBER_AUDIO 0x00c - -/* - * The number of MIDI ports, 0-8; read-only. If > 0, there will be one - * additional quadlet in each data block, following the audio quadlets. - */ -#define TX_NUMBER_MIDI 0x010 - -/* - * The speed at which the packets are sent, SCODE_100-_400; read/write. - */ -#define TX_SPEED 0x014 - -/* - * Names of all audio channels; read-only. Quadlets are byte-swapped. Names - * are separated with one backslash, the list is terminated with two - * backslashes. - */ -#define TX_NAMES 0x018 -#define TX_NAMES_SIZE 256 - -/* - * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio - * channel. - */ -#define TX_AC3_CAPABILITIES 0x118 - -/* - * Send audio data with IEC60958 label; read/write. Bitmask with one bit per - * audio channel. This register can be changed even while the stream is - * running. - */ -#define TX_AC3_ENABLE 0x11c - -/* - * Playback stream settings. This section includes the number/size registers - * and the registers of all streams. - */ - -/* - * The number of supported playback streams; read-only. - */ -#define RX_NUMBER 0x000 - -/* - * The size of one stream's register block, in quadlets; read-only. The - * registers of the first stream follow immediately afterwards; the registers - * of the following streams are offset by this register's value. - */ -#define RX_SIZE 0x004 - -/* - * The isochronous channel number on which packets are received, or -1 if the - * stream is not to be used; read/write. - */ -#define RX_ISOCHRONOUS 0x008 - -/* - * Index of first quadlet to be interpreted; read/write. If > 0, that many - * quadlets at the beginning of each data block will be ignored, and all the - * audio and MIDI quadlets will follow. - */ -#define RX_SEQ_START 0x00c - -/* - * The number of audio channels; read-only. There will be one quadlet per - * channel. - */ -#define RX_NUMBER_AUDIO 0x010 - -/* - * The number of MIDI ports, 0-8; read-only. If > 0, there will be one - * additional quadlet in each data block, following the audio quadlets. - */ -#define RX_NUMBER_MIDI 0x014 - -/* - * Names of all audio channels; read-only. Quadlets are byte-swapped. Names - * are separated with one backslash, the list is terminated with two - * backslashes. - */ -#define RX_NAMES 0x018 -#define RX_NAMES_SIZE 256 - -/* - * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio - * channel. - */ -#define RX_AC3_CAPABILITIES 0x118 - -/* - * Receive audio data with IEC60958 label; read/write. Bitmask with one bit - * per audio channel. This register can be changed even while the stream is - * running. - */ -#define RX_AC3_ENABLE 0x11c - -/* - * Extended synchronization information. - * This section can be read completely with a block read request. - */ - -/* - * Current clock source; read-only. - */ -#define EXT_SYNC_CLOCK_SOURCE 0x000 - -/* - * Clock source is locked (boolean); read-only. - */ -#define EXT_SYNC_LOCKED 0x004 - -/* - * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or - * _NONE; read-only. - */ -#define EXT_SYNC_RATE 0x008 - -/* - * ADAT user data bits; read-only. - */ -#define EXT_SYNC_ADAT_USER_DATA 0x00c -/* The data bits, if available. */ -#define ADAT_USER_DATA_MASK 0x0f -/* The data bits are not available. */ -#define ADAT_USER_DATA_NO_DATA 0x10 - -#endif diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c deleted file mode 100644 index eafd74b..0000000 --- a/sound/firewire/dice.c +++ /dev/null @@ -1,1512 +0,0 @@ -/* - * TC Applied Technologies Digital Interface Communications Engine 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 -#include -#include -#include -#include -#include -#include -#include -#include "amdtp.h" -#include "iso-resources.h" -#include "lib.h" -#include "dice-interface.h" - - -struct snd_dice { - struct snd_card *card; - struct fw_unit *unit; - spinlock_t lock; - struct mutex mutex; - unsigned int global_offset; - unsigned int rx_offset; - unsigned int clock_caps; - unsigned int rx_channels[3]; - unsigned int rx_midi_ports[3]; - struct fw_address_handler notification_handler; - int owner_generation; - int dev_lock_count; /* > 0 driver, < 0 userspace */ - bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; - wait_queue_head_t hwdep_wait; - u32 notification_bits; - struct fw_iso_resources rx_resources; - struct amdtp_stream rx_stream; -}; - -MODULE_DESCRIPTION("DICE driver"); -MODULE_AUTHOR("Clemens Ladisch "); -MODULE_LICENSE("GPL v2"); - -static const unsigned int dice_rates[] = { - /* mode 0 */ - [0] = 32000, - [1] = 44100, - [2] = 48000, - /* mode 1 */ - [3] = 88200, - [4] = 96000, - /* mode 2 */ - [5] = 176400, - [6] = 192000, -}; - -static unsigned int rate_to_index(unsigned int rate) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice_rates[i] == rate) - return i; - - return 0; -} - -static unsigned int rate_index_to_mode(unsigned int rate_index) -{ - return ((int)rate_index - 1) / 2; -} - -static void dice_lock_changed(struct snd_dice *dice) -{ - dice->dev_lock_changed = true; - wake_up(&dice->hwdep_wait); -} - -static int dice_try_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count < 0) { - err = -EBUSY; - goto out; - } - - if (dice->dev_lock_count++ == 0) - dice_lock_changed(dice); - err = 0; - -out: - spin_unlock_irq(&dice->lock); - - return err; -} - -static void dice_unlock(struct snd_dice *dice) -{ - spin_lock_irq(&dice->lock); - - if (WARN_ON(dice->dev_lock_count <= 0)) - goto out; - - if (--dice->dev_lock_count == 0) - dice_lock_changed(dice); - -out: - spin_unlock_irq(&dice->lock); -} - -static inline u64 global_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->global_offset + offset; -} - -/* TODO: rx index */ -static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->rx_offset + offset; -} - -static int dice_owner_set(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err, errors = 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - for (;;) { - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, - TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | - dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - break; - } - if (err != -EAGAIN || ++errors >= 3) - break; - - msleep(20); - } - - kfree(buffer); - - return err; -} - -static int dice_owner_update(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err; - - if (dice->owner_generation == -1) - return 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - } else if (err == -EAGAIN) { - err = 0; /* try again later */ - } - - kfree(buffer); - - if (err < 0) - dice->owner_generation = -1; - - return err; -} - -static void dice_owner_clear(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return; - - buffer[0] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - buffer[1] = cpu_to_be64(OWNER_NO_OWNER); - snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - kfree(buffer); - - dice->owner_generation = -1; -} - -static int dice_enable_set(struct snd_dice *dice) -{ - __be32 value; - int err; - - value = cpu_to_be32(1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, - FW_FIXED_GENERATION | dice->owner_generation); - if (err < 0) - return err; - - dice->global_enabled = true; - - return 0; -} - -static void dice_enable_clear(struct snd_dice *dice) -{ - __be32 value; - - if (!dice->global_enabled) - return; - - value = 0; - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - dice->global_enabled = false; -} - -static void dice_notification(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) -{ - struct snd_dice *dice = callback_data; - u32 bits; - unsigned long flags; - - if (tcode != TCODE_WRITE_QUADLET_REQUEST) { - fw_send_response(card, request, RCODE_TYPE_ERROR); - return; - } - if ((offset & 3) != 0) { - fw_send_response(card, request, RCODE_ADDRESS_ERROR); - return; - } - - bits = be32_to_cpup(data); - - spin_lock_irqsave(&dice->lock, flags); - dice->notification_bits |= bits; - spin_unlock_irqrestore(&dice->lock, flags); - - fw_send_response(card, request, RCODE_COMPLETE); - - if (bits & NOTIFY_CLOCK_ACCEPTED) - complete(&dice->clock_accepted); - wake_up(&dice->hwdep_wait); -} - -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - const struct snd_interval *channels = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval allowed_rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, mode; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { - mode = rate_index_to_mode(i); - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(channels, dice->rx_channels[mode])) { - allowed_rates.min = min(allowed_rates.min, - dice_rates[i]); - allowed_rates.max = max(allowed_rates.max, - dice_rates[i]); - } - } - - return snd_interval_refine(rate, &allowed_rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - const struct snd_interval *rate = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval allowed_channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, mode; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, dice_rates[i])) { - mode = rate_index_to_mode(i); - allowed_channels.min = min(allowed_channels.min, - dice->rx_channels[mode]); - allowed_channels.max = max(allowed_channels.max, - dice->rx_channels[mode]); - } - - return snd_interval_refine(channels, &allowed_channels); -} - -static int dice_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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; - - err = dice_try_lock(dice); - if (err < 0) - goto error; - - runtime->hw = hardware; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(dice_rates[i]); - snd_pcm_limit_hw_rates(runtime); - - for (i = 0; i < 3; ++i) - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - goto err_lock; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - goto err_lock; - - err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); - if (err < 0) - goto err_lock; - - return 0; - -err_lock: - dice_unlock(dice); -error: - return err; -} - -static int dice_close(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - dice_unlock(dice); - - return 0; -} - -static int dice_stream_start_packets(struct snd_dice *dice) -{ - int err; - - if (amdtp_stream_running(&dice->rx_stream)) - return 0; - - err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - return err; - - err = dice_enable_set(dice); - if (err < 0) { - amdtp_stream_stop(&dice->rx_stream); - return err; - } - - return 0; -} - -static int dice_stream_start(struct snd_dice *dice) -{ - __be32 channel; - int err; - - if (!dice->rx_resources.allocated) { - err = fw_iso_resources_allocate(&dice->rx_resources, - amdtp_stream_get_max_payload(&dice->rx_stream), - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - goto error; - - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_fw_transaction(dice->unit, - TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4, 0); - if (err < 0) - goto err_resources; - } - - err = dice_stream_start_packets(dice); - if (err < 0) - goto err_rx_channel; - - return 0; - -err_rx_channel: - channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); -err_resources: - fw_iso_resources_free(&dice->rx_resources); -error: - return err; -} - -static void dice_stream_stop_packets(struct snd_dice *dice) -{ - if (amdtp_stream_running(&dice->rx_stream)) { - dice_enable_clear(dice); - amdtp_stream_stop(&dice->rx_stream); - } -} - -static void dice_stream_stop(struct snd_dice *dice) -{ - __be32 channel; - - dice_stream_stop_packets(dice); - - if (!dice->rx_resources.allocated) - return; - - channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); - - fw_iso_resources_free(&dice->rx_resources); -} - -static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) -{ - __be32 value; - int err; - - reinit_completion(&dice->clock_accepted); - - value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &value, 4, 0); - if (err < 0) - return err; - - if (!wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(100))) - dev_warn(&dice->unit->device, "clock change timed out\n"); - - return 0; -} - -static int dice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_dice *dice = substream->private_data; - unsigned int rate_index, mode, rate, channels, i; - int err; - - mutex_lock(&dice->mutex); - dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - rate = params_rate(hw_params); - rate_index = rate_to_index(rate); - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); - if (err < 0) - return err; - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ - channels = params_channels(hw_params); - if (rate_index > 4) { - if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { - err = -ENOSYS; - return err; - } - - rate /= 2; - channels *= 2; - dice->rx_stream.double_pcm_frames = true; - } else { - dice->rx_stream.double_pcm_frames = false; - } - - mode = rate_index_to_mode(rate_index); - amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, - dice->rx_midi_ports[mode]); - if (rate_index > 4) { - channels /= 2; - - for (i = 0; i < channels; i++) { - dice->rx_stream.pcm_positions[i] = i * 2; - dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; - } - } - - amdtp_stream_set_pcm_format(&dice->rx_stream, - params_format(hw_params)); - - return 0; -} - -static int dice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - mutex_lock(&dice->mutex); - dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int dice_prepare(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - int err; - - mutex_lock(&dice->mutex); - - if (amdtp_streaming_error(&dice->rx_stream)) - dice_stream_stop_packets(dice); - - err = dice_stream_start(dice); - if (err < 0) { - mutex_unlock(&dice->mutex); - return err; - } - - mutex_unlock(&dice->mutex); - - amdtp_stream_pcm_prepare(&dice->rx_stream); - - return 0; -} - -static int dice_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_dice *dice = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&dice->rx_stream, pcm); - - return 0; -} - -static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - return amdtp_stream_pcm_pointer(&dice->rx_stream); -} - -static int dice_create_pcm(struct snd_dice *dice) -{ - static struct snd_pcm_ops ops = { - .open = dice_open, - .close = dice_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dice_hw_params, - .hw_free = dice_hw_free, - .prepare = dice_prepare, - .trigger = dice_trigger, - .pointer = dice_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; - - return 0; -} - -static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, - long count, loff_t *offset) -{ - struct snd_dice *dice = hwdep->private_data; - DEFINE_WAIT(wait); - union snd_firewire_event event; - - spin_lock_irq(&dice->lock); - - while (!dice->dev_lock_changed && dice->notification_bits == 0) { - prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irq(&dice->lock); - schedule(); - finish_wait(&dice->hwdep_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dice->lock); - } - - memset(&event, 0, sizeof(event)); - if (dice->dev_lock_changed) { - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = dice->dev_lock_count > 0; - dice->dev_lock_changed = false; - - count = min_t(long, count, sizeof(event.lock_status)); - } else { - event.dice_notification.type = - SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; - event.dice_notification.notification = dice->notification_bits; - dice->notification_bits = 0; - - count = min_t(long, count, sizeof(event.dice_notification)); - } - - spin_unlock_irq(&dice->lock); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; - - return count; -} - -static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, - poll_table *wait) -{ - struct snd_dice *dice = hwdep->private_data; - unsigned int events; - - poll_wait(file, &dice->hwdep_wait, wait); - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_changed || dice->notification_bits != 0) - events = POLLIN | POLLRDNORM; - else - events = 0; - spin_unlock_irq(&dice->lock); - - return events; -} - -static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) -{ - struct fw_device *dev = fw_parent_device(dice->unit); - struct snd_firewire_get_info info; - - memset(&info, 0, sizeof(info)); - info.type = SNDRV_FIREWIRE_TYPE_DICE; - info.card = dev->card->index; - *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); - *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); - strlcpy(info.device_name, dev_name(&dev->device), - sizeof(info.device_name)); - - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - -static int dice_hwdep_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == 0) { - dice->dev_lock_count = -1; - err = 0; - } else { - err = -EBUSY; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_unlock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == -1) { - dice->dev_lock_count = 0; - err = 0; - } else { - err = -EBADFD; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) -{ - struct snd_dice *dice = hwdep->private_data; - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_count == -1) - dice->dev_lock_count = 0; - spin_unlock_irq(&dice->lock); - - return 0; -} - -static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct snd_dice *dice = hwdep->private_data; - - switch (cmd) { - case SNDRV_FIREWIRE_IOCTL_GET_INFO: - return dice_hwdep_get_info(dice, (void __user *)arg); - case SNDRV_FIREWIRE_IOCTL_LOCK: - return dice_hwdep_lock(dice); - case SNDRV_FIREWIRE_IOCTL_UNLOCK: - return dice_hwdep_unlock(dice); - default: - return -ENOIOCTLCMD; - } -} - -#ifdef CONFIG_COMPAT -static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dice_hwdep_ioctl(hwdep, file, cmd, - (unsigned long)compat_ptr(arg)); -} -#else -#define dice_hwdep_compat_ioctl NULL -#endif - -static int dice_create_hwdep(struct snd_dice *dice) -{ - static const struct snd_hwdep_ops ops = { - .read = dice_hwdep_read, - .release = dice_hwdep_release, - .poll = dice_hwdep_poll, - .ioctl = dice_hwdep_ioctl, - .ioctl_compat = dice_hwdep_compat_ioctl, - }; - struct snd_hwdep *hwdep; - int err; - - err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); - if (err < 0) - return err; - strcpy(hwdep->name, "DICE"); - hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; - hwdep->ops = ops; - hwdep->private_data = dice; - hwdep->exclusive = true; - - return 0; -} - -static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, - unsigned int offset_q, unsigned int quadlets) -{ - unsigned int i; - int err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + 4 * offset_q, - buffer, 4 * quadlets, 0); - if (err < 0) - return err; - - for (i = 0; i < quadlets; ++i) - be32_to_cpus(&((u32 *)buffer)[i]); - - return 0; -} - -static const char *str_from_array(const char *const strs[], unsigned int count, - unsigned int i) -{ - if (i < count) - return strs[i]; - - return "(unknown)"; -} - -static void dice_proc_fixup_string(char *s, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < size; i += 4) - cpu_to_le32s((u32 *)(s + i)); - - for (i = 0; i < size - 2; ++i) { - if (s[i] == '\0') - return; - if (s[i] == '\\' && s[i + 1] == '\\') { - s[i + 2] = '\0'; - return; - } - } - s[size - 1] = '\0'; -} - -static void dice_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - static const char *const section_names[5] = { - "global", "tx", "rx", "ext_sync", "unused2" - }; - static const char *const clock_sources[] = { - "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", - "wc", "arx1", "arx2", "arx3", "arx4", "internal" - }; - static const char *const rates[] = { - "32000", "44100", "48000", "88200", "96000", "176400", "192000", - "any low", "any mid", "any high", "none" - }; - struct snd_dice *dice = entry->private_data; - u32 sections[ARRAY_SIZE(section_names) * 2]; - struct { - u32 number; - u32 size; - } tx_rx_header; - union { - struct { - u32 owner_hi, owner_lo; - u32 notification; - char nick_name[NICK_NAME_SIZE]; - u32 clock_select; - u32 enable; - u32 status; - u32 extended_status; - u32 sample_rate; - u32 version; - u32 clock_caps; - char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; - } global; - struct { - u32 iso; - u32 number_audio; - u32 number_midi; - u32 speed; - char names[TX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } tx; - struct { - u32 iso; - u32 seq_start; - u32 number_audio; - u32 number_midi; - char names[RX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } rx; - struct { - u32 clock_source; - u32 locked; - u32 rate; - u32 adat_user_data; - } ext_sync; - } buf; - unsigned int quadlets, stream, i; - - if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) - return; - snd_iprintf(buffer, "sections:\n"); - for (i = 0; i < ARRAY_SIZE(section_names); ++i) - snd_iprintf(buffer, " %s: offset %u, size %u\n", - section_names[i], - sections[i * 2], sections[i * 2 + 1]); - - quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); - if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) - return; - snd_iprintf(buffer, "global:\n"); - snd_iprintf(buffer, " owner: %04x:%04x%08x\n", - buf.global.owner_hi >> 16, - buf.global.owner_hi & 0xffff, buf.global.owner_lo); - snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); - dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); - snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); - snd_iprintf(buffer, " clock select: %s %s\n", - str_from_array(clock_sources, ARRAY_SIZE(clock_sources), - buf.global.clock_select & CLOCK_SOURCE_MASK), - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.clock_select & CLOCK_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " enable: %u\n", buf.global.enable); - snd_iprintf(buffer, " status: %slocked %s\n", - buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.status & - STATUS_NOMINAL_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); - snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); - snd_iprintf(buffer, " version: %u.%u.%u.%u\n", - (buf.global.version >> 24) & 0xff, - (buf.global.version >> 16) & 0xff, - (buf.global.version >> 8) & 0xff, - (buf.global.version >> 0) & 0xff); - if (quadlets >= 90) { - snd_iprintf(buffer, " clock caps:"); - for (i = 0; i <= 6; ++i) - if (buf.global.clock_caps & (1 << i)) - snd_iprintf(buffer, " %s", rates[i]); - for (i = 0; i <= 12; ++i) - if (buf.global.clock_caps & (1 << (16 + i))) - snd_iprintf(buffer, " %s", clock_sources[i]); - snd_iprintf(buffer, "\n"); - dice_proc_fixup_string(buf.global.clock_source_names, - CLOCK_SOURCE_NAMES_SIZE); - snd_iprintf(buffer, " clock source names: %s\n", - buf.global.clock_source_names); - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "tx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); - snd_iprintf(buffer, " audio channels: %u\n", - buf.tx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); - snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.tx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.tx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.tx.ac3_enable); - } - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "rx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); - snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); - snd_iprintf(buffer, " audio channels: %u\n", - buf.rx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.rx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.rx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.rx.ac3_enable); - } - } - - quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); - if (quadlets >= 4) { - if (dice_proc_read_mem(dice, &buf.ext_sync, - sections[6], 4) < 0) - return; - snd_iprintf(buffer, "ext status:\n"); - snd_iprintf(buffer, " clock source: %s\n", - str_from_array(clock_sources, - ARRAY_SIZE(clock_sources), - buf.ext_sync.clock_source)); - snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); - snd_iprintf(buffer, " rate: %s\n", - str_from_array(rates, ARRAY_SIZE(rates), - buf.ext_sync.rate)); - snd_iprintf(buffer, " adat user data: "); - if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) - snd_iprintf(buffer, "-\n"); - else - snd_iprintf(buffer, "%x\n", - buf.ext_sync.adat_user_data); - } -} - -static void dice_create_proc(struct snd_dice *dice) -{ - struct snd_info_entry *entry; - - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); -} - -static void dice_card_free(struct snd_card *card) -{ - struct snd_dice *dice = card->private_data; - - amdtp_stream_destroy(&dice->rx_stream); - fw_core_remove_address_handler(&dice->notification_handler); - mutex_destroy(&dice->mutex); -} - -#define OUI_WEISS 0x001c6a - -#define DICE_CATEGORY_ID 0x04 -#define WEISS_CATEGORY_ID 0x00 - -static int dice_interface_check(struct fw_unit *unit) -{ - static const int min_values[10] = { - 10, 0x64 / 4, - 10, 0x18 / 4, - 10, 0x18 / 4, - 0, 0, - 0, 0, - }; - struct fw_device *device = fw_parent_device(unit); - struct fw_csr_iterator it; - int key, value, vendor = -1, model = -1, err; - unsigned int category, i; - __be32 pointers[ARRAY_SIZE(min_values)]; - __be32 tx_data[4]; - __be32 version; - - /* - * Check that GUID and unit directory are constructed according to DICE - * rules, i.e., that the specifier ID is the GUID's OUI, and that the - * GUID chip ID consists of the 8-bit category ID, the 10-bit product - * ID, and a 22-bit serial number. - */ - fw_csr_iterator_init(&it, unit->directory); - while (fw_csr_iterator_next(&it, &key, &value)) { - switch (key) { - case CSR_SPECIFIER_ID: - vendor = value; - break; - case CSR_MODEL: - model = value; - break; - } - } - if (vendor == OUI_WEISS) - category = WEISS_CATEGORY_ID; - else - category = DICE_CATEGORY_ID; - if (device->config_rom[3] != ((vendor << 8) | category) || - device->config_rom[4] >> 22 != model) - return -ENODEV; - - /* - * Check that the sub address spaces exist and are located inside the - * private address space. The minimum values are chosen so that all - * minimally required registers are included. - */ - err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return -ENODEV; - for (i = 0; i < ARRAY_SIZE(pointers); ++i) { - value = be32_to_cpu(pointers[i]); - if (value < min_values[i] || value >= 0x40000) - return -ENODEV; - } - - /* We support playback only. Let capture devices be handled by FFADO. */ - err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[2]) * 4, - tx_data, sizeof(tx_data), 0); - if (err < 0 || (tx_data[0] && tx_data[3])) - return -ENODEV; - - /* - * Check that the implemented DICE driver specification major version - * number matches. - */ - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, - &version, 4, 0); - if (err < 0) - return -ENODEV; - if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { - dev_err(&unit->device, - "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - return -ENODEV; - } - - return 0; -} - -static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) -{ - int i; - - for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) - if ((dice->clock_caps & (1 << i)) && - rate_index_to_mode(i) == mode) - return i; - - return -1; -} - -static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) -{ - __be32 values[2]; - int rate_index, err; - - rate_index = highest_supported_mode_rate(dice, mode); - if (rate_index < 0) { - dice->rx_channels[mode] = 0; - dice->rx_midi_ports[mode] = 0; - return 0; - } - - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); - if (err < 0) - return err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - rx_address(dice, RX_NUMBER_AUDIO), - values, 2 * 4, 0); - if (err < 0) - return err; - - dice->rx_channels[mode] = be32_to_cpu(values[0]); - dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); - - return 0; -} - -static int dice_read_params(struct snd_dice *dice) -{ - __be32 pointers[6]; - __be32 value; - int mode, err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return err; - - dice->global_offset = be32_to_cpu(pointers[0]) * 4; - dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - - /* some very old firmwares don't tell about their clock support */ - if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { - err = snd_fw_transaction( - dice->unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_CAPABILITIES), - &value, 4, 0); - if (err < 0) - return err; - dice->clock_caps = be32_to_cpu(value); - } else { - /* this should be supported by any device */ - dice->clock_caps = CLOCK_CAP_RATE_44100 | - CLOCK_CAP_RATE_48000 | - CLOCK_CAP_SOURCE_ARX1 | - CLOCK_CAP_SOURCE_INTERNAL; - } - - for (mode = 2; mode >= 0; --mode) { - err = dice_read_mode_params(dice, mode); - if (err < 0) - return err; - } - - return 0; -} - -static void dice_card_strings(struct snd_dice *dice) -{ - struct snd_card *card = dice->card; - struct fw_device *dev = fw_parent_device(dice->unit); - char vendor[32], model[32]; - unsigned int i; - int err; - - strcpy(card->driver, "DICE"); - - strcpy(card->shortname, "DICE"); - BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - global_address(dice, GLOBAL_NICK_NAME), - card->shortname, sizeof(card->shortname), 0); - if (err >= 0) { - /* DICE strings are returned in "always-wrong" endianness */ - BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); - for (i = 0; i < sizeof(card->shortname); i += 4) - swab32s((u32 *)&card->shortname[i]); - card->shortname[sizeof(card->shortname) - 1] = '\0'; - } - - strcpy(vendor, "?"); - fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); - strcpy(model, "?"); - fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); - snprintf(card->longname, sizeof(card->longname), - "%s %s (serial %u) at %s, S%d", - vendor, model, dev->config_rom[4] & 0x3fffff, - dev_name(&dice->unit->device), 100 << dev->max_speed); - - strcpy(card->mixername, "DICE"); -} - -static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) -{ - struct snd_card *card; - struct snd_dice *dice; - __be32 clock_sel; - int err; - - err = dice_interface_check(unit); - if (err < 0) - return err; - - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*dice), &card); - if (err < 0) - return err; - - dice = card->private_data; - dice->card = card; - spin_lock_init(&dice->lock); - mutex_init(&dice->mutex); - dice->unit = unit; - init_completion(&dice->clock_accepted); - init_waitqueue_head(&dice->hwdep_wait); - - dice->notification_handler.length = 4; - dice->notification_handler.address_callback = dice_notification; - dice->notification_handler.callback_data = dice; - err = fw_core_add_address_handler(&dice->notification_handler, - &fw_high_memory_region); - if (err < 0) - goto err_mutex; - - err = dice_owner_set(dice); - if (err < 0) - goto err_notification_handler; - - err = dice_read_params(dice); - if (err < 0) - goto err_owner; - - err = fw_iso_resources_init(&dice->rx_resources, unit); - if (err < 0) - goto err_owner; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) - goto err_resources; - - card->private_free = dice_card_free; - - dice_card_strings(dice); - - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); - clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - - err = dice_create_pcm(dice); - if (err < 0) - goto error; - - err = dice_create_hwdep(dice); - if (err < 0) - goto error; - - dice_create_proc(dice); - - err = snd_card_register(card); - if (err < 0) - goto error; - - dev_set_drvdata(&unit->device, dice); - - return 0; - -err_resources: - fw_iso_resources_destroy(&dice->rx_resources); -err_owner: - dice_owner_clear(dice); -err_notification_handler: - fw_core_remove_address_handler(&dice->notification_handler); -err_mutex: - mutex_destroy(&dice->mutex); -error: - snd_card_free(card); - return err; -} - -static void dice_remove(struct fw_unit *unit) -{ - struct snd_dice *dice = dev_get_drvdata(&unit->device); - - amdtp_stream_pcm_abort(&dice->rx_stream); - - snd_card_disconnect(dice->card); - - mutex_lock(&dice->mutex); - - dice_stream_stop(dice); - dice_owner_clear(dice); - - mutex_unlock(&dice->mutex); - - snd_card_free_when_closed(dice->card); -} - -static void dice_bus_reset(struct fw_unit *unit) -{ - struct snd_dice *dice = dev_get_drvdata(&unit->device); - - /* - * On a bus reset, the DICE firmware disables streaming and then goes - * off contemplating its own navel for hundreds of milliseconds before - * it can react to any of our attempts to reenable streaming. This - * means that we lose synchronization anyway, so we force our streams - * to stop so that the application can restart them in an orderly - * manner. - */ - amdtp_stream_pcm_abort(&dice->rx_stream); - - mutex_lock(&dice->mutex); - - dice->global_enabled = false; - dice_stream_stop_packets(dice); - - dice_owner_update(dice); - - fw_iso_resources_update(&dice->rx_resources); - - mutex_unlock(&dice->mutex); -} - -#define DICE_INTERFACE 0x000001 - -static const struct ieee1394_device_id dice_id_table[] = { - { - .match_flags = IEEE1394_MATCH_VERSION, - .version = DICE_INTERFACE, - }, - { } -}; -MODULE_DEVICE_TABLE(ieee1394, dice_id_table); - -static struct fw_driver dice_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - .bus = &fw_bus_type, - }, - .probe = dice_probe, - .update = dice_bus_reset, - .remove = dice_remove, - .id_table = dice_id_table, -}; - -static int __init alsa_dice_init(void) -{ - return driver_register(&dice_driver.driver); -} - -static void __exit alsa_dice_exit(void) -{ - driver_unregister(&dice_driver.driver); -} - -module_init(alsa_dice_init); -module_exit(alsa_dice_exit); diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile new file mode 100644 index 0000000..af05d7e --- /dev/null +++ b/sound/firewire/dice/Makefile @@ -0,0 +1,2 @@ +snd-dice-objs := dice.o +obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h new file mode 100644 index 0000000..27b044f --- /dev/null +++ b/sound/firewire/dice/dice-interface.h @@ -0,0 +1,371 @@ +#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED +#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED + +/* + * DICE device interface definitions + */ + +/* + * Generally, all registers can be read like memory, i.e., with quadlet read or + * block read transactions with at least quadlet-aligned offset and length. + * Writes are not allowed except where noted; quadlet-sized registers must be + * written with a quadlet write transaction. + * + * All values are in big endian. The DICE firmware runs on a little-endian CPU + * and just byte-swaps _all_ quadlets on the bus, so values without endianness + * (e.g. strings) get scrambled and must be byte-swapped again by the driver. + */ + +/* + * Streaming is handled by the "DICE driver" interface. Its registers are + * located in this private address space. + */ +#define DICE_PRIVATE_SPACE 0xffffe0000000uLL + +/* + * The registers are organized in several sections, which are organized + * separately to allow them to be extended individually. Whether a register is + * supported can be detected by checking its offset against its section's size. + * + * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/ + * size values are measured in quadlets. Read-only. + */ +#define DICE_GLOBAL_OFFSET 0x00 +#define DICE_GLOBAL_SIZE 0x04 +#define DICE_TX_OFFSET 0x08 +#define DICE_TX_SIZE 0x0c +#define DICE_RX_OFFSET 0x10 +#define DICE_RX_SIZE 0x14 +#define DICE_EXT_SYNC_OFFSET 0x18 +#define DICE_EXT_SYNC_SIZE 0x1c +#define DICE_UNUSED2_OFFSET 0x20 +#define DICE_UNUSED2_SIZE 0x24 + +/* + * Global settings. + */ + +/* + * Stores the full 64-bit address (node ID and offset in the node's address + * space) where the device will send notifications. Must be changed with + * a compare/swap transaction by the owner. This register is automatically + * cleared on a bus reset. + */ +#define GLOBAL_OWNER 0x000 +#define OWNER_NO_OWNER 0xffff000000000000uLL +#define OWNER_NODE_SHIFT 48 + +/* + * A bitmask with asynchronous events; read-only. When any event(s) happen, + * the bits of previous events are cleared, and the value of this register is + * also written to the address stored in the owner register. + */ +#define GLOBAL_NOTIFICATION 0x008 +/* Some registers in the Rx/Tx sections may have changed. */ +#define NOTIFY_RX_CFG_CHG 0x00000001 +#define NOTIFY_TX_CFG_CHG 0x00000002 +/* Lock status of the current clock source may have changed. */ +#define NOTIFY_LOCK_CHG 0x00000010 +/* Write to the clock select register has been finished. */ +#define NOTIFY_CLOCK_ACCEPTED 0x00000020 +/* Lock status of some clock source has changed. */ +#define NOTIFY_EXT_STATUS 0x00000040 +/* Other bits may be used for device-specific events. */ + +/* + * A name that can be customized for each device; read/write. Padded with zero + * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver + * happens to be using. + */ +#define GLOBAL_NICK_NAME 0x00c +#define NICK_NAME_SIZE 64 + +/* + * The current sample rate and clock source; read/write. Whether a clock + * source or sample rate is supported is device-specific; the internal clock + * source is always available. Low/mid/high = up to 48/96/192 kHz. This + * register can be changed even while streams are running. + */ +#define GLOBAL_CLOCK_SELECT 0x04c +#define CLOCK_SOURCE_MASK 0x000000ff +#define CLOCK_SOURCE_AES1 0x00000000 +#define CLOCK_SOURCE_AES2 0x00000001 +#define CLOCK_SOURCE_AES3 0x00000002 +#define CLOCK_SOURCE_AES4 0x00000003 +#define CLOCK_SOURCE_AES_ANY 0x00000004 +#define CLOCK_SOURCE_ADAT 0x00000005 +#define CLOCK_SOURCE_TDIF 0x00000006 +#define CLOCK_SOURCE_WC 0x00000007 +#define CLOCK_SOURCE_ARX1 0x00000008 +#define CLOCK_SOURCE_ARX2 0x00000009 +#define CLOCK_SOURCE_ARX3 0x0000000a +#define CLOCK_SOURCE_ARX4 0x0000000b +#define CLOCK_SOURCE_INTERNAL 0x0000000c +#define CLOCK_RATE_MASK 0x0000ff00 +#define CLOCK_RATE_32000 0x00000000 +#define CLOCK_RATE_44100 0x00000100 +#define CLOCK_RATE_48000 0x00000200 +#define CLOCK_RATE_88200 0x00000300 +#define CLOCK_RATE_96000 0x00000400 +#define CLOCK_RATE_176400 0x00000500 +#define CLOCK_RATE_192000 0x00000600 +#define CLOCK_RATE_ANY_LOW 0x00000700 +#define CLOCK_RATE_ANY_MID 0x00000800 +#define CLOCK_RATE_ANY_HIGH 0x00000900 +#define CLOCK_RATE_NONE 0x00000a00 +#define CLOCK_RATE_SHIFT 8 + +/* + * Enable streaming; read/write. Writing a non-zero value (re)starts all + * streams that have a valid iso channel set; zero stops all streams. The + * streams' parameters must be configured before starting. This register is + * automatically cleared on a bus reset. + */ +#define GLOBAL_ENABLE 0x050 + +/* + * Status of the sample clock; read-only. + */ +#define GLOBAL_STATUS 0x054 +/* The current clock source is locked. */ +#define STATUS_SOURCE_LOCKED 0x00000001 +/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */ +#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 + +/* + * Status of all clock sources; read-only. + */ +#define GLOBAL_EXTENDED_STATUS 0x058 +/* + * The _LOCKED bits always show the current status; any change generates + * a notification. + */ +#define EXT_STATUS_AES1_LOCKED 0x00000001 +#define EXT_STATUS_AES2_LOCKED 0x00000002 +#define EXT_STATUS_AES3_LOCKED 0x00000004 +#define EXT_STATUS_AES4_LOCKED 0x00000008 +#define EXT_STATUS_ADAT_LOCKED 0x00000010 +#define EXT_STATUS_TDIF_LOCKED 0x00000020 +#define EXT_STATUS_ARX1_LOCKED 0x00000040 +#define EXT_STATUS_ARX2_LOCKED 0x00000080 +#define EXT_STATUS_ARX3_LOCKED 0x00000100 +#define EXT_STATUS_ARX4_LOCKED 0x00000200 +#define EXT_STATUS_WC_LOCKED 0x00000400 +/* + * The _SLIP bits do not generate notifications; a set bit indicates that an + * error occurred since the last time when this register was read with + * a quadlet read transaction. + */ +#define EXT_STATUS_AES1_SLIP 0x00010000 +#define EXT_STATUS_AES2_SLIP 0x00020000 +#define EXT_STATUS_AES3_SLIP 0x00040000 +#define EXT_STATUS_AES4_SLIP 0x00080000 +#define EXT_STATUS_ADAT_SLIP 0x00100000 +#define EXT_STATUS_TDIF_SLIP 0x00200000 +#define EXT_STATUS_ARX1_SLIP 0x00400000 +#define EXT_STATUS_ARX2_SLIP 0x00800000 +#define EXT_STATUS_ARX3_SLIP 0x01000000 +#define EXT_STATUS_ARX4_SLIP 0x02000000 +#define EXT_STATUS_WC_SLIP 0x04000000 + +/* + * The measured rate of the current clock source, in Hz; read-only. + */ +#define GLOBAL_SAMPLE_RATE 0x05c + +/* + * The version of the DICE driver specification that this device conforms to; + * read-only. + */ +#define GLOBAL_VERSION 0x060 + +/* Some old firmware versions do not have the following global registers: */ + +/* + * Supported sample rates and clock sources; read-only. + */ +#define GLOBAL_CLOCK_CAPABILITIES 0x064 +#define CLOCK_CAP_RATE_32000 0x00000001 +#define CLOCK_CAP_RATE_44100 0x00000002 +#define CLOCK_CAP_RATE_48000 0x00000004 +#define CLOCK_CAP_RATE_88200 0x00000008 +#define CLOCK_CAP_RATE_96000 0x00000010 +#define CLOCK_CAP_RATE_176400 0x00000020 +#define CLOCK_CAP_RATE_192000 0x00000040 +#define CLOCK_CAP_SOURCE_AES1 0x00010000 +#define CLOCK_CAP_SOURCE_AES2 0x00020000 +#define CLOCK_CAP_SOURCE_AES3 0x00040000 +#define CLOCK_CAP_SOURCE_AES4 0x00080000 +#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 +#define CLOCK_CAP_SOURCE_ADAT 0x00200000 +#define CLOCK_CAP_SOURCE_TDIF 0x00400000 +#define CLOCK_CAP_SOURCE_WC 0x00800000 +#define CLOCK_CAP_SOURCE_ARX1 0x01000000 +#define CLOCK_CAP_SOURCE_ARX2 0x02000000 +#define CLOCK_CAP_SOURCE_ARX3 0x04000000 +#define CLOCK_CAP_SOURCE_ARX4 0x08000000 +#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 + +/* + * Names of all clock sources; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. Unused clock sources are included. + */ +#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 +#define CLOCK_SOURCE_NAMES_SIZE 256 + +/* + * Capture stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported capture streams; read-only. + */ +#define TX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define TX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are sent, or -1 if the + * stream is not to be used; read/write. + */ +#define TX_ISOCHRONOUS 0x008 + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel; the first channel is the first quadlet in a data block. + */ +#define TX_NUMBER_AUDIO 0x00c + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define TX_NUMBER_MIDI 0x010 + +/* + * The speed at which the packets are sent, SCODE_100-_400; read/write. + */ +#define TX_SPEED 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define TX_NAMES 0x018 +#define TX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define TX_AC3_CAPABILITIES 0x118 + +/* + * Send audio data with IEC60958 label; read/write. Bitmask with one bit per + * audio channel. This register can be changed even while the stream is + * running. + */ +#define TX_AC3_ENABLE 0x11c + +/* + * Playback stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported playback streams; read-only. + */ +#define RX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define RX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are received, or -1 if the + * stream is not to be used; read/write. + */ +#define RX_ISOCHRONOUS 0x008 + +/* + * Index of first quadlet to be interpreted; read/write. If > 0, that many + * quadlets at the beginning of each data block will be ignored, and all the + * audio and MIDI quadlets will follow. + */ +#define RX_SEQ_START 0x00c + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel. + */ +#define RX_NUMBER_AUDIO 0x010 + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define RX_NUMBER_MIDI 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define RX_NAMES 0x018 +#define RX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define RX_AC3_CAPABILITIES 0x118 + +/* + * Receive audio data with IEC60958 label; read/write. Bitmask with one bit + * per audio channel. This register can be changed even while the stream is + * running. + */ +#define RX_AC3_ENABLE 0x11c + +/* + * Extended synchronization information. + * This section can be read completely with a block read request. + */ + +/* + * Current clock source; read-only. + */ +#define EXT_SYNC_CLOCK_SOURCE 0x000 + +/* + * Clock source is locked (boolean); read-only. + */ +#define EXT_SYNC_LOCKED 0x004 + +/* + * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or + * _NONE; read-only. + */ +#define EXT_SYNC_RATE 0x008 + +/* + * ADAT user data bits; read-only. + */ +#define EXT_SYNC_ADAT_USER_DATA 0x00c +/* The data bits, if available. */ +#define ADAT_USER_DATA_MASK 0x0f +/* The data bits are not available. */ +#define ADAT_USER_DATA_NO_DATA 0x10 + +#endif diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c new file mode 100644 index 0000000..d3ec778 --- /dev/null +++ b/sound/firewire/dice/dice.c @@ -0,0 +1,1512 @@ +/* + * TC Applied Technologies Digital Interface Communications Engine 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 +#include +#include +#include +#include +#include +#include +#include +#include "../amdtp.h" +#include "../iso-resources.h" +#include "../lib.h" +#include "dice-interface.h" + + +struct snd_dice { + struct snd_card *card; + struct fw_unit *unit; + spinlock_t lock; + struct mutex mutex; + unsigned int global_offset; + unsigned int rx_offset; + unsigned int clock_caps; + unsigned int rx_channels[3]; + unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; + int owner_generation; + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; + bool global_enabled; + struct completion clock_accepted; + wait_queue_head_t hwdep_wait; + u32 notification_bits; + struct fw_iso_resources rx_resources; + struct amdtp_stream rx_stream; +}; + +MODULE_DESCRIPTION("DICE driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); + +static const unsigned int dice_rates[] = { + /* mode 0 */ + [0] = 32000, + [1] = 44100, + [2] = 48000, + /* mode 1 */ + [3] = 88200, + [4] = 96000, + /* mode 2 */ + [5] = 176400, + [6] = 192000, +}; + +static unsigned int rate_to_index(unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice_rates[i] == rate) + return i; + + return 0; +} + +static unsigned int rate_index_to_mode(unsigned int rate_index) +{ + return ((int)rate_index - 1) / 2; +} + +static void dice_lock_changed(struct snd_dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +static int dice_try_lock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; + +out: + spin_unlock_irq(&dice->lock); + + return err; +} + +static void dice_unlock(struct snd_dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); + +out: + spin_unlock_irq(&dice->lock); +} + +static inline u64 global_address(struct snd_dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->global_offset + offset; +} + +/* TODO: rx index */ +static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->rx_offset + offset; +} + +static int dice_owner_set(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int err, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, + TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (err != -EAGAIN || ++errors >= 3) + break; + + msleep(20); + } + + kfree(buffer); + + return err; +} + +static int dice_owner_update(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int err; + + if (dice->owner_generation == -1) + return 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + } else if (err == -EAGAIN) { + err = 0; /* try again later */ + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void dice_owner_clear(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return; + + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + kfree(buffer); + + dice->owner_generation = -1; +} + +static int dice_enable_set(struct snd_dice *dice) +{ + __be32 value; + int err; + + value = cpu_to_be32(1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + return err; + + dice->global_enabled = true; + + return 0; +} + +static void dice_enable_clear(struct snd_dice *dice) +{ + __be32 value; + + if (!dice->global_enabled) + return; + + value = 0; + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_dice *dice = callback_data; + u32 bits; + unsigned long flags; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + bits = be32_to_cpup(data); + + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= bits; + spin_unlock_irqrestore(&dice->lock, flags); + + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); + wake_up(&dice->hwdep_wait); +} + +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + const struct snd_interval *channels = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval allowed_rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { + mode = rate_index_to_mode(i); + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(channels, dice->rx_channels[mode])) { + allowed_rates.min = min(allowed_rates.min, + dice_rates[i]); + allowed_rates.max = max(allowed_rates.max, + dice_rates[i]); + } + } + + return snd_interval_refine(rate, &allowed_rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + const struct snd_interval *rate = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval allowed_channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(rate, dice_rates[i])) { + mode = rate_index_to_mode(i); + allowed_channels.min = min(allowed_channels.min, + dice->rx_channels[mode]); + allowed_channels.max = max(allowed_channels.max, + dice->rx_channels[mode]); + } + + return snd_interval_refine(channels, &allowed_channels); +} + +static int dice_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 = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = UINT_MAX, + .channels_max = 0, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int i; + int err; + + err = dice_try_lock(dice); + if (err < 0) + goto error; + + runtime->hw = hardware; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice->clock_caps & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(dice_rates[i]); + snd_pcm_limit_hw_rates(runtime); + + for (i = 0; i < 3; ++i) + if (dice->rx_channels[i]) { + runtime->hw.channels_min = min(runtime->hw.channels_min, + dice->rx_channels[i]); + runtime->hw.channels_max = max(runtime->hw.channels_max, + dice->rx_channels[i]); + } + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, dice, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, dice, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto err_lock; + + err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + if (err < 0) + goto err_lock; + + return 0; + +err_lock: + dice_unlock(dice); +error: + return err; +} + +static int dice_close(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + dice_unlock(dice); + + return 0; +} + +static int dice_stream_start_packets(struct snd_dice *dice) +{ + int err; + + if (amdtp_stream_running(&dice->rx_stream)) + return 0; + + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; + + err = dice_enable_set(dice); + if (err < 0) { + amdtp_stream_stop(&dice->rx_stream); + return err; + } + + return 0; +} + +static int dice_stream_start(struct snd_dice *dice) +{ + __be32 channel; + int err; + + if (!dice->rx_resources.allocated) { + err = fw_iso_resources_allocate(&dice->rx_resources, + amdtp_stream_get_max_payload(&dice->rx_stream), + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto error; + + channel = cpu_to_be32(dice->rx_resources.channel); + err = snd_fw_transaction(dice->unit, + TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4, 0); + if (err < 0) + goto err_resources; + } + + err = dice_stream_start_packets(dice); + if (err < 0) + goto err_rx_channel; + + return 0; + +err_rx_channel: + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); +err_resources: + fw_iso_resources_free(&dice->rx_resources); +error: + return err; +} + +static void dice_stream_stop_packets(struct snd_dice *dice) +{ + if (amdtp_stream_running(&dice->rx_stream)) { + dice_enable_clear(dice); + amdtp_stream_stop(&dice->rx_stream); + } +} + +static void dice_stream_stop(struct snd_dice *dice) +{ + __be32 channel; + + dice_stream_stop_packets(dice); + + if (!dice->rx_resources.allocated) + return; + + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + + fw_iso_resources_free(&dice->rx_resources); +} + +static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) +{ + __be32 value; + int err; + + reinit_completion(&dice->clock_accepted); + + value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &value, 4, 0); + if (err < 0) + return err; + + if (!wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(100))) + dev_warn(&dice->unit->device, "clock change timed out\n"); + + return 0; +} + +static int dice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + unsigned int rate_index, mode, rate, channels, i; + int err; + + mutex_lock(&dice->mutex); + dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + rate = params_rate(hw_params); + rate_index = rate_to_index(rate); + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + /* + * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + * one data block of AMDTP packet. Thus sampling transfer frequency is + * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + * transferred on AMDTP packets at 96 kHz. Two successive samples of a + * channel are stored consecutively in the packet. This quirk is called + * as 'Dual Wire'. + * For this quirk, blocking mode is required and PCM buffer size should + * be aligned to SYT_INTERVAL. + */ + channels = params_channels(hw_params); + if (rate_index > 4) { + if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { + err = -ENOSYS; + return err; + } + + rate /= 2; + channels *= 2; + dice->rx_stream.double_pcm_frames = true; + } else { + dice->rx_stream.double_pcm_frames = false; + } + + mode = rate_index_to_mode(rate_index); + amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, + dice->rx_midi_ports[mode]); + if (rate_index > 4) { + channels /= 2; + + for (i = 0; i < channels; i++) { + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; + } + } + + amdtp_stream_set_pcm_format(&dice->rx_stream, + params_format(hw_params)); + + return 0; +} + +static int dice_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int dice_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_streaming_error(&dice->rx_stream)) + dice_stream_stop_packets(dice); + + err = dice_stream_start(dice); + if (err < 0) { + mutex_unlock(&dice->mutex); + return err; + } + + mutex_unlock(&dice->mutex); + + amdtp_stream_pcm_prepare(&dice->rx_stream); + + return 0; +} + +static int dice_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&dice->rx_stream, pcm); + + return 0; +} + +static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->rx_stream); +} + +static int dice_create_pcm(struct snd_dice *dice) +{ + static struct snd_pcm_ops ops = { + .open = dice_open, + .close = dice_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dice_hw_params, + .hw_free = dice_hw_free, + .prepare = dice_prepare, + .trigger = dice_trigger, + .pointer = dice_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; + + return 0; +} + +static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + struct snd_dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } else { + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min_t(long, count, sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; +} + +static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int dice_hwdep_lock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int dice_hwdep_unlock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + +static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return dice_hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return dice_hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return dice_hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return dice_hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define dice_hwdep_compat_ioctl NULL +#endif + +static int dice_create_hwdep(struct snd_dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = dice_hwdep_read, + .release = dice_hwdep_release, + .poll = dice_hwdep_poll, + .ioctl = dice_hwdep_ioctl, + .ioctl_compat = dice_hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} + +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\\' && s[i + 1] == '\\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct snd_dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +static void dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} + +static void dice_card_free(struct snd_card *card) +{ + struct snd_dice *dice = card->private_data; + + amdtp_stream_destroy(&dice->rx_stream); + fw_core_remove_address_handler(&dice->notification_handler); + mutex_destroy(&dice->mutex); +} + +#define OUI_WEISS 0x001c6a + +#define DICE_CATEGORY_ID 0x04 +#define WEISS_CATEGORY_ID 0x00 + +static int dice_interface_check(struct fw_unit *unit) +{ + static const int min_values[10] = { + 10, 0x64 / 4, + 10, 0x18 / 4, + 10, 0x18 / 4, + 0, 0, + 0, 0, + }; + struct fw_device *device = fw_parent_device(unit); + struct fw_csr_iterator it; + int key, value, vendor = -1, model = -1, err; + unsigned int category, i; + __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 tx_data[4]; + __be32 version; + + /* + * Check that GUID and unit directory are constructed according to DICE + * rules, i.e., that the specifier ID is the GUID's OUI, and that the + * GUID chip ID consists of the 8-bit category ID, the 10-bit product + * ID, and a 22-bit serial number. + */ + fw_csr_iterator_init(&it, unit->directory); + while (fw_csr_iterator_next(&it, &key, &value)) { + switch (key) { + case CSR_SPECIFIER_ID: + vendor = value; + break; + case CSR_MODEL: + model = value; + break; + } + } + if (vendor == OUI_WEISS) + category = WEISS_CATEGORY_ID; + else + category = DICE_CATEGORY_ID; + if (device->config_rom[3] != ((vendor << 8) | category) || + device->config_rom[4] >> 22 != model) + return -ENODEV; + + /* + * Check that the sub address spaces exist and are located inside the + * private address space. The minimum values are chosen so that all + * minimally required registers are included. + */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers), 0); + if (err < 0) + return -ENODEV; + for (i = 0; i < ARRAY_SIZE(pointers); ++i) { + value = be32_to_cpu(pointers[i]); + if (value < min_values[i] || value >= 0x40000) + return -ENODEV; + } + + /* We support playback only. Let capture devices be handled by FFADO. */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[2]) * 4, + tx_data, sizeof(tx_data), 0); + if (err < 0 || (tx_data[0] && tx_data[3])) + return -ENODEV; + + /* + * Check that the implemented DICE driver specification major version + * number matches. + */ + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, 4, 0); + if (err < 0) + return -ENODEV; + if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { + dev_err(&unit->device, + "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); + return -ENODEV; + } + + return 0; +} + +static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) +{ + int i; + + for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) + if ((dice->clock_caps & (1 << i)) && + rate_index_to_mode(i) == mode) + return i; + + return -1; +} + +static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) +{ + __be32 values[2]; + int rate_index, err; + + rate_index = highest_supported_mode_rate(dice, mode); + if (rate_index < 0) { + dice->rx_channels[mode] = 0; + dice->rx_midi_ports[mode] = 0; + return 0; + } + + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + rx_address(dice, RX_NUMBER_AUDIO), + values, 2 * 4, 0); + if (err < 0) + return err; + + dice->rx_channels[mode] = be32_to_cpu(values[0]); + dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); + + return 0; +} + +static int dice_read_params(struct snd_dice *dice) +{ + __be32 pointers[6]; + __be32 value; + int mode, err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers), 0); + if (err < 0) + return err; + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + + /* some very old firmwares don't tell about their clock support */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { + err = snd_fw_transaction( + dice->unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_CAPABILITIES), + &value, 4, 0); + if (err < 0) + return err; + dice->clock_caps = be32_to_cpu(value); + } else { + /* this should be supported by any device */ + dice->clock_caps = CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000 | + CLOCK_CAP_SOURCE_ARX1 | + CLOCK_CAP_SOURCE_INTERNAL; + } + + for (mode = 2; mode >= 0; --mode) { + err = dice_read_mode_params(dice, mode); + if (err < 0) + return err; + } + + return 0; +} + +static void dice_card_strings(struct snd_dice *dice) +{ + struct snd_card *card = dice->card; + struct fw_device *dev = fw_parent_device(dice->unit); + char vendor[32], model[32]; + unsigned int i; + int err; + + strcpy(card->driver, "DICE"); + + strcpy(card->shortname, "DICE"); + BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + global_address(dice, GLOBAL_NICK_NAME), + card->shortname, sizeof(card->shortname), 0); + if (err >= 0) { + /* DICE strings are returned in "always-wrong" endianness */ + BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); + for (i = 0; i < sizeof(card->shortname); i += 4) + swab32s((u32 *)&card->shortname[i]); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } + + strcpy(vendor, "?"); + fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); + strcpy(model, "?"); + fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); + snprintf(card->longname, sizeof(card->longname), + "%s %s (serial %u) at %s, S%d", + vendor, model, dev->config_rom[4] & 0x3fffff, + dev_name(&dice->unit->device), 100 << dev->max_speed); + + strcpy(card->mixername, "DICE"); +} + +static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct snd_dice *dice; + __be32 clock_sel; + int err; + + err = dice_interface_check(unit); + if (err < 0) + return err; + + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(*dice), &card); + if (err < 0) + return err; + + dice = card->private_data; + dice->card = card; + spin_lock_init(&dice->lock); + mutex_init(&dice->mutex); + dice->unit = unit; + init_completion(&dice->clock_accepted); + init_waitqueue_head(&dice->hwdep_wait); + + dice->notification_handler.length = 4; + dice->notification_handler.address_callback = dice_notification; + dice->notification_handler.callback_data = dice; + err = fw_core_add_address_handler(&dice->notification_handler, + &fw_high_memory_region); + if (err < 0) + goto err_mutex; + + err = dice_owner_set(dice); + if (err < 0) + goto err_notification_handler; + + err = dice_read_params(dice); + if (err < 0) + goto err_owner; + + err = fw_iso_resources_init(&dice->rx_resources, unit); + if (err < 0) + goto err_owner; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, + CIP_BLOCKING); + if (err < 0) + goto err_resources; + + card->private_free = dice_card_free; + + dice_card_strings(dice); + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4, 0); + if (err < 0) + goto error; + clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); + clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4, 0); + if (err < 0) + goto error; + + err = dice_create_pcm(dice); + if (err < 0) + goto error; + + err = dice_create_hwdep(dice); + if (err < 0) + goto error; + + dice_create_proc(dice); + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, dice); + + return 0; + +err_resources: + fw_iso_resources_destroy(&dice->rx_resources); +err_owner: + dice_owner_clear(dice); +err_notification_handler: + fw_core_remove_address_handler(&dice->notification_handler); +err_mutex: + mutex_destroy(&dice->mutex); +error: + snd_card_free(card); + return err; +} + +static void dice_remove(struct fw_unit *unit) +{ + struct snd_dice *dice = dev_get_drvdata(&unit->device); + + amdtp_stream_pcm_abort(&dice->rx_stream); + + snd_card_disconnect(dice->card); + + mutex_lock(&dice->mutex); + + dice_stream_stop(dice); + dice_owner_clear(dice); + + mutex_unlock(&dice->mutex); + + snd_card_free_when_closed(dice->card); +} + +static void dice_bus_reset(struct fw_unit *unit) +{ + struct snd_dice *dice = dev_get_drvdata(&unit->device); + + /* + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + amdtp_stream_pcm_abort(&dice->rx_stream); + + mutex_lock(&dice->mutex); + + dice->global_enabled = false; + dice_stream_stop_packets(dice); + + dice_owner_update(dice); + + fw_iso_resources_update(&dice->rx_resources); + + mutex_unlock(&dice->mutex); +} + +#define DICE_INTERFACE 0x000001 + +static const struct ieee1394_device_id dice_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, dice_id_table); + +static struct fw_driver dice_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = dice_probe, + .update = dice_bus_reset, + .remove = dice_remove, + .id_table = dice_id_table, +}; + +static int __init alsa_dice_init(void) +{ + return driver_register(&dice_driver.driver); +} + +static void __exit alsa_dice_exit(void) +{ + driver_unregister(&dice_driver.driver); +} + +module_init(alsa_dice_init); +module_exit(alsa_dice_exit); -- cgit v0.10.2 From 7c2d4c0cf5bacb42bc3079e61d299dfaa3dbdde5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:13 +0900 Subject: ALSA: dice: Split transaction functionality into a file This commit adds a file with some helper functions for transaction, and move some codes into the file with some arrangements. For Dice chipset, well-known FCP or AV/C commands are not used to control devices. It's achieved by read/write transactions into specific addresses. Dice's address area is split into 5 areas. Each area has its own role. The offset for each area can be got by reading head of the address area. By reading these areas, drivers can get to know device status. By writing these areas, drivers can change device status. Dice has a specific mechanism called as 'notification'. When device status is changed, Dice devices tells the event by sending transaction. This notification is sent to an address which drivers register in advance. But this causes an issue to drivers. To handle the notification, drivers need to allocate its own callback function to the address region in host controller. This region is exclusive. For the other applications, drivers must give a mechanism to read the received notification. For this purpose, Dice driver already implements hwdep interface. Dice chipset doesn't allow drivers to register several addresses. In this reason, when this driver is applied to a device, the other drivers should _not_ try to register its own address to the device. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index af05d7e..9f473cb 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice.o +snd-dice-objs := dice-transaction.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c new file mode 100644 index 0000000..a9b98e0 --- /dev/null +++ b/sound/firewire/dice/dice-transaction.c @@ -0,0 +1,387 @@ +/* + * dice_transaction.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +#define NOTIFICATION_TIMEOUT_MS 100 + +static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, + u64 offset) +{ + switch (type) { + case SND_DICE_ADDR_TYPE_TX: + offset += dice->tx_offset; + break; + case SND_DICE_ADDR_TYPE_RX: + offset += dice->rx_offset; + break; + case SND_DICE_ADDR_TYPE_SYNC: + offset += dice->sync_offset; + break; + case SND_DICE_ADDR_TYPE_RSRV: + offset += dice->rsrv_offset; + break; + case SND_DICE_ADDR_TYPE_GLOBAL: + default: + offset += dice->global_offset; + break; + }; + offset += DICE_PRIVATE_SPACE; + return offset; +} + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_WRITE_QUADLET_REQUEST : + TCODE_WRITE_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_READ_QUADLET_REQUEST : + TCODE_READ_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) +{ + return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + info, 4); +} + +static int set_clock_info(struct snd_dice *dice, + unsigned int rate, unsigned int source) +{ + unsigned int retries = 3; + unsigned int i; + __be32 info; + u32 mask; + u32 clock; + int err; +retry: + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + clock = be32_to_cpu(info); + if (source != UINT_MAX) { + mask = CLOCK_SOURCE_MASK; + clock &= ~mask; + clock |= source; + } + if (rate != UINT_MAX) { + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (snd_dice_rates[i] == rate) + break; + } + if (i == ARRAY_SIZE(snd_dice_rates)) { + err = -EINVAL; + goto end; + } + + mask = CLOCK_RATE_MASK; + clock &= ~mask; + clock |= i << CLOCK_RATE_SHIFT; + } + info = cpu_to_be32(clock); + + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); + + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + &info, 4); + if (err < 0) + goto end; + + /* Timeout means it's invalid request, probably bus reset occurred. */ + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + if (retries-- == 0) { + err = -ETIMEDOUT; + goto end; + } + + err = snd_dice_transaction_reinit(dice); + if (err < 0) + goto end; + + msleep(500); /* arbitrary */ + goto retry; + } +end: + return err; +} + +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source) +{ + __be32 info; + int err; + + err = get_clock_info(dice, &info); + if (err >= 0) + *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK; + + return err; +} +int snd_dice_transaction_set_clock_source(struct snd_dice *dice, + unsigned int source) +{ + return set_clock_info(dice, UINT_MAX, source); +} + +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) +{ + __be32 info; + unsigned int index; + int err; + + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; + if (index >= SND_DICE_RATES_COUNT) { + err = -ENOSYS; + goto end; + } + + *rate = snd_dice_rates[index]; +end: + return err; +} +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) +{ + return set_clock_info(dice, rate, UINT_MAX); +} + +int snd_dice_transaction_set_enable(struct snd_dice *dice) +{ + __be32 value; + int err = 0; + + if (dice->global_enabled) + goto end; + + value = cpu_to_be32(1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + goto end; + + dice->global_enabled = true; +end: + return err; +} + +void snd_dice_transaction_clear_enable(struct snd_dice *dice) +{ + __be32 value; + + value = 0; + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_dice *dice = callback_data; + u32 bits; + unsigned long flags; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + bits = be32_to_cpup(data); + + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= bits; + spin_unlock_irqrestore(&dice->lock, flags); + + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); + wake_up(&dice->hwdep_wait); +} + +static int register_notification_address(struct snd_dice *dice, bool retry) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + unsigned int retries; + int err; + + retries = (retry) ? 3 : 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, + SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + if (err == 0) { + /* success */ + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) + break; + /* The address seems to be already registered. */ + if (buffer[0] == buffer[1]) + break; + + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + if (err != -EAGAIN || retries-- > 0) + break; + + msleep(20); + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void unregister_notification_address(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (buffer == NULL) + return; + + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + kfree(buffer); + + dice->owner_generation = -1; +} + +void snd_dice_transaction_destroy(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return; + + unregister_notification_address(dice); + + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; +} + +int snd_dice_transaction_reinit(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return -EINVAL; + + return register_notification_address(dice, false); +} + +int snd_dice_transaction_init(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + __be32 *pointers; + int err; + + /* Use the same way which dice_interface_check() does. */ + pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + /* Get offsets for sub-addresses */ + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(__be32) * 10, 0); + if (err < 0) + goto end; + + /* Allocation callback in address space over host controller */ + handler->length = 4; + handler->address_callback = dice_notification; + handler->callback_data = dice; + err = fw_core_add_address_handler(handler, &fw_high_memory_region); + if (err < 0) { + handler->callback_data = NULL; + goto end; + } + + /* Register the address space */ + err = register_notification_address(dice, true); + if (err < 0) { + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; + goto end; + } + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + dice->tx_offset = be32_to_cpu(pointers[2]) * 4; + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + dice->sync_offset = be32_to_cpu(pointers[6]) * 4; + dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; + + /* Set up later. */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) + dice->clock_caps = 1; +end: + kfree(pointers); + return err; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index d3ec778..dd62316 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -5,60 +5,13 @@ * 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 -#include -#include -#include -#include -#include -#include -#include -#include "../amdtp.h" -#include "../iso-resources.h" -#include "../lib.h" -#include "dice-interface.h" - - -struct snd_dice { - struct snd_card *card; - struct fw_unit *unit; - spinlock_t lock; - struct mutex mutex; - unsigned int global_offset; - unsigned int rx_offset; - unsigned int clock_caps; - unsigned int rx_channels[3]; - unsigned int rx_midi_ports[3]; - struct fw_address_handler notification_handler; - int owner_generation; - int dev_lock_count; /* > 0 driver, < 0 userspace */ - bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; - wait_queue_head_t hwdep_wait; - u32 notification_bits; - struct fw_iso_resources rx_resources; - struct amdtp_stream rx_stream; -}; +#include "dice.h" MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); -static const unsigned int dice_rates[] = { +const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ [0] = 32000, [1] = 44100, @@ -75,8 +28,8 @@ static unsigned int rate_to_index(unsigned int rate) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice_rates[i] == rate) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) + if (snd_dice_rates[i] == rate) return i; return 0; @@ -128,192 +81,6 @@ out: spin_unlock_irq(&dice->lock); } -static inline u64 global_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->global_offset + offset; -} - -/* TODO: rx index */ -static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->rx_offset + offset; -} - -static int dice_owner_set(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err, errors = 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - for (;;) { - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, - TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | - dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - break; - } - if (err != -EAGAIN || ++errors >= 3) - break; - - msleep(20); - } - - kfree(buffer); - - return err; -} - -static int dice_owner_update(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err; - - if (dice->owner_generation == -1) - return 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - } else if (err == -EAGAIN) { - err = 0; /* try again later */ - } - - kfree(buffer); - - if (err < 0) - dice->owner_generation = -1; - - return err; -} - -static void dice_owner_clear(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return; - - buffer[0] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - buffer[1] = cpu_to_be64(OWNER_NO_OWNER); - snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - kfree(buffer); - - dice->owner_generation = -1; -} - -static int dice_enable_set(struct snd_dice *dice) -{ - __be32 value; - int err; - - value = cpu_to_be32(1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, - FW_FIXED_GENERATION | dice->owner_generation); - if (err < 0) - return err; - - dice->global_enabled = true; - - return 0; -} - -static void dice_enable_clear(struct snd_dice *dice) -{ - __be32 value; - - if (!dice->global_enabled) - return; - - value = 0; - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - dice->global_enabled = false; -} - -static void dice_notification(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) -{ - struct snd_dice *dice = callback_data; - u32 bits; - unsigned long flags; - - if (tcode != TCODE_WRITE_QUADLET_REQUEST) { - fw_send_response(card, request, RCODE_TYPE_ERROR); - return; - } - if ((offset & 3) != 0) { - fw_send_response(card, request, RCODE_ADDRESS_ERROR); - return; - } - - bits = be32_to_cpup(data); - - spin_lock_irqsave(&dice->lock, flags); - dice->notification_bits |= bits; - spin_unlock_irqrestore(&dice->lock, flags); - - fw_send_response(card, request, RCODE_COMPLETE); - - if (bits & NOTIFY_CLOCK_ACCEPTED) - complete(&dice->clock_accepted); - wake_up(&dice->hwdep_wait); -} - static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -327,14 +94,14 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, }; unsigned int i, mode; - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { mode = rate_index_to_mode(i); if ((dice->clock_caps & (1 << i)) && snd_interval_test(channels, dice->rx_channels[mode])) { allowed_rates.min = min(allowed_rates.min, - dice_rates[i]); + snd_dice_rates[i]); allowed_rates.max = max(allowed_rates.max, - dice_rates[i]); + snd_dice_rates[i]); } } @@ -354,9 +121,9 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, }; unsigned int i, mode; - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, dice_rates[i])) { + snd_interval_test(rate, snd_dice_rates[i])) { mode = rate_index_to_mode(i); allowed_channels.min = min(allowed_channels.min, dice->rx_channels[mode]); @@ -395,10 +162,10 @@ static int dice_open(struct snd_pcm_substream *substream) runtime->hw = hardware; - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) if (dice->clock_caps & (1 << i)) runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(dice_rates[i]); + snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); snd_pcm_limit_hw_rates(runtime); for (i = 0; i < 3; ++i) @@ -453,7 +220,7 @@ static int dice_stream_start_packets(struct snd_dice *dice) if (err < 0) return err; - err = dice_enable_set(dice); + err = snd_dice_transaction_set_enable(dice); if (err < 0) { amdtp_stream_stop(&dice->rx_stream); return err; @@ -475,10 +242,8 @@ static int dice_stream_start(struct snd_dice *dice) goto error; channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_fw_transaction(dice->unit, - TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4, 0); + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); if (err < 0) goto err_resources; } @@ -491,8 +256,7 @@ static int dice_stream_start(struct snd_dice *dice) err_rx_channel: channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); err_resources: fw_iso_resources_free(&dice->rx_resources); error: @@ -502,7 +266,7 @@ error: static void dice_stream_stop_packets(struct snd_dice *dice) { if (amdtp_stream_running(&dice->rx_stream)) { - dice_enable_clear(dice); + snd_dice_transaction_clear_enable(dice); amdtp_stream_stop(&dice->rx_stream); } } @@ -517,33 +281,11 @@ static void dice_stream_stop(struct snd_dice *dice) return; channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); fw_iso_resources_free(&dice->rx_resources); } -static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) -{ - __be32 value; - int err; - - reinit_completion(&dice->clock_accepted); - - value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &value, 4, 0); - if (err < 0) - return err; - - if (!wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(100))) - dev_warn(&dice->unit->device, "clock change timed out\n"); - - return 0; -} - static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { @@ -561,8 +303,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, return err; rate = params_rate(hw_params); - rate_index = rate_to_index(rate); - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) return err; @@ -577,6 +318,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, * be aligned to SYT_INTERVAL. */ channels = params_channels(hw_params); + rate_index = rate_to_index(rate); if (rate_index > 4) { if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { err = -ENOSYS; @@ -1118,15 +860,6 @@ static void dice_create_proc(struct snd_dice *dice) snd_info_set_text_ops(entry, dice, dice_proc_read); } -static void dice_card_free(struct snd_card *card) -{ - struct snd_dice *dice = card->private_data; - - amdtp_stream_destroy(&dice->rx_stream); - fw_core_remove_address_handler(&dice->notification_handler); - mutex_destroy(&dice->mutex); -} - #define OUI_WEISS 0x001c6a #define DICE_CATEGORY_ID 0x04 @@ -1143,12 +876,17 @@ static int dice_interface_check(struct fw_unit *unit) }; struct fw_device *device = fw_parent_device(unit); struct fw_csr_iterator it; - int key, value, vendor = -1, model = -1, err; + int key, val, vendor = -1, model = -1, err; unsigned int category, i; - __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 *pointers, value; __be32 tx_data[4]; __be32 version; + pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), + GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + /* * Check that GUID and unit directory are constructed according to DICE * rules, i.e., that the specifier ID is the GUID's OUI, and that the @@ -1156,13 +894,13 @@ static int dice_interface_check(struct fw_unit *unit) * ID, and a 22-bit serial number. */ fw_csr_iterator_init(&it, unit->directory); - while (fw_csr_iterator_next(&it, &key, &value)) { + while (fw_csr_iterator_next(&it, &key, &val)) { switch (key) { case CSR_SPECIFIER_ID: - vendor = value; + vendor = val; break; case CSR_MODEL: - model = value; + model = val; break; } } @@ -1171,8 +909,10 @@ static int dice_interface_check(struct fw_unit *unit) else category = DICE_CATEGORY_ID; if (device->config_rom[3] != ((vendor << 8) | category) || - device->config_rom[4] >> 22 != model) - return -ENODEV; + device->config_rom[4] >> 22 != model) { + err = -ENODEV; + goto end; + } /* * Check that the sub address spaces exist and are located inside the @@ -1180,14 +920,18 @@ static int dice_interface_check(struct fw_unit *unit) * minimally required registers are included. */ err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return -ENODEV; - for (i = 0; i < ARRAY_SIZE(pointers); ++i) { + DICE_PRIVATE_SPACE, pointers, + sizeof(__be32) * ARRAY_SIZE(min_values), 0); + if (err < 0) { + err = -ENODEV; + goto end; + } + for (i = 0; i < ARRAY_SIZE(min_values); ++i) { value = be32_to_cpu(pointers[i]); - if (value < min_values[i] || value >= 0x40000) - return -ENODEV; + if (value < min_values[i] || value >= 0x40000) { + err = -ENODEV; + goto end; + } } /* We support playback only. Let capture devices be handled by FFADO. */ @@ -1195,8 +939,10 @@ static int dice_interface_check(struct fw_unit *unit) DICE_PRIVATE_SPACE + be32_to_cpu(pointers[2]) * 4, tx_data, sizeof(tx_data), 0); - if (err < 0 || (tx_data[0] && tx_data[3])) - return -ENODEV; + if (err < 0 || (tx_data[0] && tx_data[3])) { + err = -ENODEV; + goto end; + } /* * Check that the implemented DICE driver specification major version @@ -1206,22 +952,25 @@ static int dice_interface_check(struct fw_unit *unit) DICE_PRIVATE_SPACE + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, &version, 4, 0); - if (err < 0) - return -ENODEV; + if (err < 0) { + err = -ENODEV; + goto end; + } if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { dev_err(&unit->device, "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - return -ENODEV; + err = -ENODEV; + goto end; } - - return 0; +end: + return err; } static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) { int i; - for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) + for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i) if ((dice->clock_caps & (1 << i)) && rate_index_to_mode(i) == mode) return i; @@ -1241,13 +990,12 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) return 0; } - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]); if (err < 0) return err; - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - rx_address(dice, RX_NUMBER_AUDIO), - values, 2 * 4, 0); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + values, sizeof(values)); if (err < 0) return err; @@ -1259,25 +1007,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) static int dice_read_params(struct snd_dice *dice) { - __be32 pointers[6]; __be32 value; int mode, err; - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return err; - - dice->global_offset = be32_to_cpu(pointers[0]) * 4; - dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - /* some very old firmwares don't tell about their clock support */ - if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { - err = snd_fw_transaction( - dice->unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_CAPABILITIES), - &value, 4, 0); + if (dice->clock_caps > 0) { + err = snd_dice_transaction_read_global(dice, + GLOBAL_CLOCK_CAPABILITIES, + &value, 4); if (err < 0) return err; dice->clock_caps = be32_to_cpu(value); @@ -1310,9 +1047,9 @@ static void dice_card_strings(struct snd_dice *dice) strcpy(card->shortname, "DICE"); BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - global_address(dice, GLOBAL_NICK_NAME), - card->shortname, sizeof(card->shortname), 0); + err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME, + card->shortname, + sizeof(card->shortname)); if (err >= 0) { /* DICE strings are returned in "always-wrong" endianness */ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); @@ -1333,70 +1070,50 @@ static void dice_card_strings(struct snd_dice *dice) strcpy(card->mixername, "DICE"); } +static void dice_card_free(struct snd_card *card) +{ + struct snd_dice *dice = card->private_data; + + snd_dice_transaction_destroy(dice); + mutex_destroy(&dice->mutex); +} + static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_card *card; struct snd_dice *dice; - __be32 clock_sel; int err; err = dice_interface_check(unit); if (err < 0) - return err; + goto end; err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card); if (err < 0) - return err; + goto end; dice = card->private_data; dice->card = card; + dice->unit = unit; + card->private_free = dice_card_free; + spin_lock_init(&dice->lock); mutex_init(&dice->mutex); - dice->unit = unit; init_completion(&dice->clock_accepted); init_waitqueue_head(&dice->hwdep_wait); - dice->notification_handler.length = 4; - dice->notification_handler.address_callback = dice_notification; - dice->notification_handler.callback_data = dice; - err = fw_core_add_address_handler(&dice->notification_handler, - &fw_high_memory_region); - if (err < 0) - goto err_mutex; - - err = dice_owner_set(dice); + err = snd_dice_transaction_init(dice); if (err < 0) - goto err_notification_handler; + goto error; err = dice_read_params(dice); if (err < 0) - goto err_owner; - - err = fw_iso_resources_init(&dice->rx_resources, unit); - if (err < 0) - goto err_owner; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) - goto err_resources; - - card->private_free = dice_card_free; + goto error; dice_card_strings(dice); - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); - clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); if (err < 0) goto error; @@ -1410,22 +1127,28 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice_create_proc(dice); - err = snd_card_register(card); + err = fw_iso_resources_init(&dice->rx_resources, unit); if (err < 0) goto error; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - dev_set_drvdata(&unit->device, dice); + err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, + CIP_BLOCKING); + if (err < 0) { + fw_iso_resources_destroy(&dice->rx_resources); + goto error; + } - return 0; + err = snd_card_register(card); + if (err < 0) { + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); + goto error; + } -err_resources: - fw_iso_resources_destroy(&dice->rx_resources); -err_owner: - dice_owner_clear(dice); -err_notification_handler: - fw_core_remove_address_handler(&dice->notification_handler); -err_mutex: - mutex_destroy(&dice->mutex); + dev_set_drvdata(&unit->device, dice); +end: + return err; error: snd_card_free(card); return err; @@ -1442,7 +1165,6 @@ static void dice_remove(struct fw_unit *unit) mutex_lock(&dice->mutex); dice_stream_stop(dice); - dice_owner_clear(dice); mutex_unlock(&dice->mutex); @@ -1453,6 +1175,9 @@ static void dice_bus_reset(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device); + /* The handler address register becomes initialized. */ + snd_dice_transaction_reinit(dice); + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -1466,10 +1191,8 @@ static void dice_bus_reset(struct fw_unit *unit) mutex_lock(&dice->mutex); dice->global_enabled = false; - dice_stream_stop_packets(dice); - - dice_owner_update(dice); + dice_stream_stop_packets(dice); fw_iso_resources_update(&dice->rx_resources); mutex_unlock(&dice->mutex); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h new file mode 100644 index 0000000..c756e62 --- /dev/null +++ b/sound/firewire/dice/dice.h @@ -0,0 +1,160 @@ +/* + * dice.h - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_DICE_H_INCLUDED +#define SOUND_DICE_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../amdtp.h" +#include "../iso-resources.h" +#include "../lib.h" +#include "dice-interface.h" + +struct snd_dice { + struct snd_card *card; + struct fw_unit *unit; + spinlock_t lock; + struct mutex mutex; + + /* Offsets for sub-addresses */ + unsigned int global_offset; + unsigned int rx_offset; + unsigned int tx_offset; + unsigned int sync_offset; + unsigned int rsrv_offset; + + unsigned int clock_caps; + unsigned int rx_channels[3]; + unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; + int owner_generation; + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; + bool global_enabled; + struct completion clock_accepted; + wait_queue_head_t hwdep_wait; + u32 notification_bits; + struct fw_iso_resources rx_resources; + struct amdtp_stream rx_stream; +}; + +enum snd_dice_addr_type { + SND_DICE_ADDR_TYPE_PRIVATE, + SND_DICE_ADDR_TYPE_GLOBAL, + SND_DICE_ADDR_TYPE_TX, + SND_DICE_ADDR_TYPE_RX, + SND_DICE_ADDR_TYPE_SYNC, + SND_DICE_ADDR_TYPE_RSRV, +}; + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, + void *buf, unsigned int len); +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len); + +static inline int snd_dice_transaction_write_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_read_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_write_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} +static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} + +int snd_dice_transaction_set_clock_source(struct snd_dice *dice, + unsigned int source); +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source); +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); +int snd_dice_transaction_set_enable(struct snd_dice *dice); +void snd_dice_transaction_clear_enable(struct snd_dice *dice); +int snd_dice_transaction_init(struct snd_dice *dice); +int snd_dice_transaction_reinit(struct snd_dice *dice); +void snd_dice_transaction_destroy(struct snd_dice *dice); + +#define SND_DICE_RATES_COUNT 7 +extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; + +#endif -- cgit v0.10.2 From 6eb6c81eee2a6270b39ca02a446f3ccece24b6f8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:14 +0900 Subject: ALSA: dice: Split stream functionality into a file This commit adds a file with some helper functions for streaming, and move some codes into the file with some arrangements. Well-known CMP is not used to start/stop streams for Dice chipset. It's achieved by writing to specific address. We call this way as 'enable'. When devices are 'enabled', streaming starts in registered isochronous channel. Some helper functions are already implemented in previous commit. Basically, the stream is compliant to IEC 61883-6, so-called as AMDTP. But Dice has a specific quirk, so called-as 'Dual Wire'. This quirk is applied at 176.4/192.0kHz. In this mode, each packet includes double number of events than number in the specification, and stream runs at a half of sampling rate. There is another quirk at bus reset. Dice chipset handles drivers' request but don't re-enable streaming. So stream should be stopped. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 9f473cb..867864c 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice-transaction.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c new file mode 100644 index 0000000..c25b9fb --- /dev/null +++ b/sound/firewire/dice/dice-stream.c @@ -0,0 +1,206 @@ +/* + * dice_stream.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { + /* mode 0 */ + [0] = 32000, + [1] = 44100, + [2] = 48000, + /* mode 1 */ + [3] = 88200, + [4] = 96000, + /* mode 2 */ + [5] = 176400, + [6] = 192000, +}; + +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + unsigned int *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (!(dice->clock_caps & BIT(i))) + continue; + if (snd_dice_rates[i] != rate) + continue; + + *mode = (i - 1) / 2; + return 0; + } + return -EINVAL; +} + +int snd_dice_stream_start_packets(struct snd_dice *dice) +{ + int err; + + if (amdtp_stream_running(&dice->rx_stream)) + return 0; + + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; + + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + amdtp_stream_stop(&dice->rx_stream); + return err; + } + + return 0; +} + +int snd_dice_stream_start(struct snd_dice *dice) +{ + __be32 channel; + int err; + + if (!dice->rx_resources.allocated) { + err = fw_iso_resources_allocate(&dice->rx_resources, + amdtp_stream_get_max_payload(&dice->rx_stream), + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto error; + + channel = cpu_to_be32(dice->rx_resources.channel); + err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS, + &channel, 4); + if (err < 0) + goto err_resources; + } + + err = snd_dice_stream_start_packets(dice); + if (err < 0) + goto err_rx_channel; + + return 0; + +err_rx_channel: + channel = cpu_to_be32((u32)-1); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); +err_resources: + fw_iso_resources_free(&dice->rx_resources); +error: + return err; +} + +void snd_dice_stream_stop_packets(struct snd_dice *dice) +{ + if (amdtp_stream_running(&dice->rx_stream)) { + snd_dice_transaction_clear_enable(dice); + amdtp_stream_stop(&dice->rx_stream); + } +} + +void snd_dice_stream_stop(struct snd_dice *dice) +{ + __be32 channel; + + snd_dice_stream_stop_packets(dice); + + if (!dice->rx_resources.allocated) + return; + + channel = cpu_to_be32((u32)-1); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); + + fw_iso_resources_free(&dice->rx_resources); +} + +int snd_dice_stream_init(struct snd_dice *dice) +{ + int err; + + err = fw_iso_resources_init(&dice->rx_resources, dice->unit); + if (err < 0) + goto end; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM, + CIP_BLOCKING); + if (err < 0) + goto error; + + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + if (err < 0) + goto error; +end: + return err; +error: + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); + return err; +} + +void snd_dice_stream_destroy(struct snd_dice *dice) +{ + amdtp_stream_pcm_abort(&dice->rx_stream); + snd_dice_stream_stop(dice); + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); +} + +void snd_dice_stream_update(struct snd_dice *dice) +{ + /* + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + dice->global_enabled = false; + + amdtp_stream_pcm_abort(&dice->rx_stream); + snd_dice_stream_stop_packets(dice); + fw_iso_resources_update(&dice->rx_resources); +} + +static void dice_lock_changed(struct snd_dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +int snd_dice_stream_lock_try(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; +out: + spin_unlock_irq(&dice->lock); + return err; +} + +void snd_dice_stream_lock_release(struct snd_dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); +out: + spin_unlock_irq(&dice->lock); +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index dd62316..e032b1c 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,127 +11,62 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); -const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { - /* mode 0 */ - [0] = 32000, - [1] = 44100, - [2] = 48000, - /* mode 1 */ - [3] = 88200, - [4] = 96000, - /* mode 2 */ - [5] = 176400, - [6] = 192000, -}; - -static unsigned int rate_to_index(unsigned int rate) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if (snd_dice_rates[i] == rate) - return i; - - return 0; -} - -static unsigned int rate_index_to_mode(unsigned int rate_index) -{ - return ((int)rate_index - 1) / 2; -} - -static void dice_lock_changed(struct snd_dice *dice) -{ - dice->dev_lock_changed = true; - wake_up(&dice->hwdep_wait); -} - -static int dice_try_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count < 0) { - err = -EBUSY; - goto out; - } - - if (dice->dev_lock_count++ == 0) - dice_lock_changed(dice); - err = 0; - -out: - spin_unlock_irq(&dice->lock); - - return err; -} - -static void dice_unlock(struct snd_dice *dice) -{ - spin_lock_irq(&dice->lock); - - if (WARN_ON(dice->dev_lock_count <= 0)) - goto out; - - if (--dice->dev_lock_count == 0) - dice_lock_changed(dice); - -out: - spin_unlock_irq(&dice->lock); -} - static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_dice *dice = rule->private; - const struct snd_interval *channels = + + const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval allowed_rates = { + struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - mode = rate_index_to_mode(i); - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(channels, dice->rx_channels[mode])) { - allowed_rates.min = min(allowed_rates.min, - snd_dice_rates[i]); - allowed_rates.max = max(allowed_rates.max, - snd_dice_rates[i]); - } + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); } - return snd_interval_refine(rate, &allowed_rates); + return snd_interval_refine(r, &rates); } static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_dice *dice = rule->private; - const struct snd_interval *rate = + + const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval allowed_channels = { + struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, snd_dice_rates[i])) { - mode = rate_index_to_mode(i); - allowed_channels.min = min(allowed_channels.min, - dice->rx_channels[mode]); - allowed_channels.max = max(allowed_channels.max, - dice->rx_channels[mode]); - } + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; - return snd_interval_refine(channels, &allowed_channels); + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); } static int dice_open(struct snd_pcm_substream *substream) @@ -156,7 +91,7 @@ static int dice_open(struct snd_pcm_substream *substream) unsigned int i; int err; - err = dice_try_lock(dice); + err = snd_dice_stream_lock_try(dice); if (err < 0) goto error; @@ -194,7 +129,7 @@ static int dice_open(struct snd_pcm_substream *substream) return 0; err_lock: - dice_unlock(dice); + snd_dice_stream_lock_release(dice); error: return err; } @@ -203,98 +138,20 @@ static int dice_close(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - dice_unlock(dice); + snd_dice_stream_lock_release(dice); return 0; } -static int dice_stream_start_packets(struct snd_dice *dice) -{ - int err; - - if (amdtp_stream_running(&dice->rx_stream)) - return 0; - - err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - return err; - - err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - amdtp_stream_stop(&dice->rx_stream); - return err; - } - - return 0; -} - -static int dice_stream_start(struct snd_dice *dice) -{ - __be32 channel; - int err; - - if (!dice->rx_resources.allocated) { - err = fw_iso_resources_allocate(&dice->rx_resources, - amdtp_stream_get_max_payload(&dice->rx_stream), - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - goto error; - - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, 4); - if (err < 0) - goto err_resources; - } - - err = dice_stream_start_packets(dice); - if (err < 0) - goto err_rx_channel; - - return 0; - -err_rx_channel: - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); -err_resources: - fw_iso_resources_free(&dice->rx_resources); -error: - return err; -} - -static void dice_stream_stop_packets(struct snd_dice *dice) -{ - if (amdtp_stream_running(&dice->rx_stream)) { - snd_dice_transaction_clear_enable(dice); - amdtp_stream_stop(&dice->rx_stream); - } -} - -static void dice_stream_stop(struct snd_dice *dice) -{ - __be32 channel; - - dice_stream_stop_packets(dice); - - if (!dice->rx_resources.allocated) - return; - - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); - - fw_iso_resources_free(&dice->rx_resources); -} - static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - unsigned int rate_index, mode, rate, channels, i; + unsigned int mode, rate, channels, i; int err; mutex_lock(&dice->mutex); - dice_stream_stop(dice); + snd_dice_stream_stop(dice); mutex_unlock(&dice->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -307,6 +164,10 @@ static int dice_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -318,8 +179,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, * be aligned to SYT_INTERVAL. */ channels = params_channels(hw_params); - rate_index = rate_to_index(rate); - if (rate_index > 4) { + if (mode > 1) { if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { err = -ENOSYS; return err; @@ -332,10 +192,9 @@ static int dice_hw_params(struct snd_pcm_substream *substream, dice->rx_stream.double_pcm_frames = false; } - mode = rate_index_to_mode(rate_index); amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, dice->rx_midi_ports[mode]); - if (rate_index > 4) { + if (mode > 4) { channels /= 2; for (i = 0; i < channels; i++) { @@ -355,7 +214,7 @@ static int dice_hw_free(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); - dice_stream_stop(dice); + snd_dice_stream_stop(dice); mutex_unlock(&dice->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); @@ -369,9 +228,9 @@ static int dice_prepare(struct snd_pcm_substream *substream) mutex_lock(&dice->mutex); if (amdtp_streaming_error(&dice->rx_stream)) - dice_stream_stop_packets(dice); + snd_dice_stream_stop_packets(dice); - err = dice_stream_start(dice); + err = snd_dice_stream_start(dice); if (err < 0) { mutex_unlock(&dice->mutex); return err; @@ -966,31 +825,37 @@ end: return err; } -static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) +static int highest_supported_mode_rate(struct snd_dice *dice, + unsigned int mode, unsigned int *rate) { - int i; + unsigned int i, m; - for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i) - if ((dice->clock_caps & (1 << i)) && - rate_index_to_mode(i) == mode) - return i; + for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { + *rate = snd_dice_rates[i - 1]; + if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) + continue; + if (mode == m) + break; + } + if (i == 0) + return -EINVAL; - return -1; + return 0; } static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) { __be32 values[2]; - int rate_index, err; + unsigned int rate; + int err; - rate_index = highest_supported_mode_rate(dice, mode); - if (rate_index < 0) { + if (highest_supported_mode_rate(dice, mode, &rate) < 0) { dice->rx_channels[mode] = 0; dice->rx_midi_ports[mode] = 0; return 0; } - err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) return err; @@ -1113,10 +978,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice_card_strings(dice); - err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); - if (err < 0) - goto error; - err = dice_create_pcm(dice); if (err < 0) goto error; @@ -1127,22 +988,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice_create_proc(dice); - err = fw_iso_resources_init(&dice->rx_resources, unit); + err = snd_dice_stream_init(dice); if (err < 0) goto error; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) { - fw_iso_resources_destroy(&dice->rx_resources); - goto error; - } err = snd_card_register(card); if (err < 0) { - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); + snd_dice_stream_destroy(dice); goto error; } @@ -1158,13 +1010,11 @@ static void dice_remove(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device); - amdtp_stream_pcm_abort(&dice->rx_stream); - snd_card_disconnect(dice->card); mutex_lock(&dice->mutex); - dice_stream_stop(dice); + snd_dice_stream_destroy(dice); mutex_unlock(&dice->mutex); @@ -1178,23 +1028,8 @@ static void dice_bus_reset(struct fw_unit *unit) /* The handler address register becomes initialized. */ snd_dice_transaction_reinit(dice); - /* - * On a bus reset, the DICE firmware disables streaming and then goes - * off contemplating its own navel for hundreds of milliseconds before - * it can react to any of our attempts to reenable streaming. This - * means that we lose synchronization anyway, so we force our streams - * to stop so that the application can restart them in an orderly - * manner. - */ - amdtp_stream_pcm_abort(&dice->rx_stream); - mutex_lock(&dice->mutex); - - dice->global_enabled = false; - - dice_stream_stop_packets(dice); - fw_iso_resources_update(&dice->rx_resources); - + snd_dice_stream_update(dice); mutex_unlock(&dice->mutex); } diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index c756e62..ca4090d 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -157,4 +157,18 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, + unsigned int rate, unsigned int *mode); + +int snd_dice_stream_start_packets(struct snd_dice *dice); +int snd_dice_stream_start(struct snd_dice *dice); +void snd_dice_stream_stop_packets(struct snd_dice *dice); +void snd_dice_stream_stop(struct snd_dice *dice); +int snd_dice_stream_init(struct snd_dice *dice); +void snd_dice_stream_destroy(struct snd_dice *dice); +void snd_dice_stream_update(struct snd_dice *dice); + +int snd_dice_stream_lock_try(struct snd_dice *dice); +void snd_dice_stream_lock_release(struct snd_dice *dice); + #endif -- cgit v0.10.2 From c50fb91f53626e3bdae3ffebfee586786f970f7c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:15 +0900 Subject: ALSA: dice: Split PCM functionality into a file This commit adds a file and move some codes related to PCM functionality. Currently PCM playback is supported. PCM capture will be supported in followed commits. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 867864c..1ddaeca 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c new file mode 100644 index 0000000..deacb53 --- /dev/null +++ b/sound/firewire/dice/dice-pcm.c @@ -0,0 +1,296 @@ +/* + * dice_pcm.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); + } + + return snd_interval_refine(r, &rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); +} + + +static int pcm_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 = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = UINT_MAX, + .channels_max = 0, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int i; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto error; + + runtime->hw = hardware; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + if (dice->clock_caps & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); + } + snd_pcm_limit_hw_rates(runtime); + + for (i = 0; i < 3; ++i) { + if (dice->rx_channels[i]) { + runtime->hw.channels_min = min(runtime->hw.channels_min, + dice->rx_channels[i]); + runtime->hw.channels_max = max(runtime->hw.channels_max, + dice->rx_channels[i]); + } + } + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, dice, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, dice, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto err_lock; + + err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + if (err < 0) + goto err_lock; + + return 0; + +err_lock: + snd_dice_stream_lock_release(dice); +error: + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + snd_dice_stream_lock_release(dice); + + return 0; +} + +static int playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + unsigned int mode, rate, channels, i; + int err; + + mutex_lock(&dice->mutex); + snd_dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + rate = params_rate(hw_params); + err = snd_dice_transaction_set_rate(dice, rate); + if (err < 0) + return err; + + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + return err; + + /* + * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + * one data block of AMDTP packet. Thus sampling transfer frequency is + * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + * transferred on AMDTP packets at 96 kHz. Two successive samples of a + * channel are stored consecutively in the packet. This quirk is called + * as 'Dual Wire'. + * For this quirk, blocking mode is required and PCM buffer size should + * be aligned to SYT_INTERVAL. + */ + channels = params_channels(hw_params); + if (mode > 1) { + if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { + err = -ENOSYS; + return err; + } + + rate /= 2; + channels *= 2; + dice->rx_stream.double_pcm_frames = true; + } else { + dice->rx_stream.double_pcm_frames = false; + } + + amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, + dice->rx_midi_ports[mode]); + if (mode > 1) { + channels /= 2; + + for (i = 0; i < channels; i++) { + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; + } + } + + amdtp_stream_set_pcm_format(&dice->rx_stream, + params_format(hw_params)); + + return 0; +} + +static int playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + snd_dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_streaming_error(&dice->rx_stream)) + snd_dice_stream_stop_packets(dice); + + err = snd_dice_stream_start(dice); + if (err < 0) { + mutex_unlock(&dice->mutex); + return err; + } + + mutex_unlock(&dice->mutex); + + amdtp_stream_pcm_prepare(&dice->rx_stream); + + return 0; +} + +static int playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->rx_stream); +} + +int snd_dice_create_pcm(struct snd_dice *dice) +{ + static struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = playback_hw_params, + .hw_free = playback_hw_free, + .prepare = playback_prepare, + .trigger = playback_trigger, + .pointer = playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + + return 0; +} diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index c25b9fb..4c4c4ff 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -96,10 +96,11 @@ error: void snd_dice_stream_stop_packets(struct snd_dice *dice) { - if (amdtp_stream_running(&dice->rx_stream)) { - snd_dice_transaction_clear_enable(dice); - amdtp_stream_stop(&dice->rx_stream); - } + if (!amdtp_stream_running(&dice->rx_stream)) + return; + + snd_dice_transaction_clear_enable(dice); + amdtp_stream_stop(&dice->rx_stream); } void snd_dice_stream_stop(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index e032b1c..b76ed06 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,292 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - - const struct snd_interval *c = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *r = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(c, pcm_channels[mode])) - continue; - - rates.min = min(rates.min, rate); - rates.max = max(rates.max, rate); - } - - return snd_interval_refine(r, &rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - - const struct snd_interval *r = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *c = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(r, rate)) - continue; - - channels.min = min(channels.min, pcm_channels[mode]); - channels.max = max(channels.max, pcm_channels[mode]); - } - - return snd_interval_refine(c, &channels); -} - -static int dice_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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; - - err = snd_dice_stream_lock_try(dice); - if (err < 0) - goto error; - - runtime->hw = hardware; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); - snd_pcm_limit_hw_rates(runtime); - - for (i = 0; i < 3; ++i) - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - goto err_lock; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - goto err_lock; - - err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); - if (err < 0) - goto err_lock; - - return 0; - -err_lock: - snd_dice_stream_lock_release(dice); -error: - return err; -} - -static int dice_close(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - snd_dice_stream_lock_release(dice); - - return 0; -} - -static int dice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_dice *dice = substream->private_data; - unsigned int mode, rate, channels, i; - int err; - - mutex_lock(&dice->mutex); - snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - rate = params_rate(hw_params); - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - err = snd_dice_stream_get_rate_mode(dice, rate, &mode); - if (err < 0) - return err; - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ - channels = params_channels(hw_params); - if (mode > 1) { - if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { - err = -ENOSYS; - return err; - } - - rate /= 2; - channels *= 2; - dice->rx_stream.double_pcm_frames = true; - } else { - dice->rx_stream.double_pcm_frames = false; - } - - amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, - dice->rx_midi_ports[mode]); - if (mode > 4) { - channels /= 2; - - for (i = 0; i < channels; i++) { - dice->rx_stream.pcm_positions[i] = i * 2; - dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; - } - } - - amdtp_stream_set_pcm_format(&dice->rx_stream, - params_format(hw_params)); - - return 0; -} - -static int dice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - mutex_lock(&dice->mutex); - snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int dice_prepare(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - int err; - - mutex_lock(&dice->mutex); - - if (amdtp_streaming_error(&dice->rx_stream)) - snd_dice_stream_stop_packets(dice); - - err = snd_dice_stream_start(dice); - if (err < 0) { - mutex_unlock(&dice->mutex); - return err; - } - - mutex_unlock(&dice->mutex); - - amdtp_stream_pcm_prepare(&dice->rx_stream); - - return 0; -} - -static int dice_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_dice *dice = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&dice->rx_stream, pcm); - - return 0; -} - -static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - return amdtp_stream_pcm_pointer(&dice->rx_stream); -} - -static int dice_create_pcm(struct snd_dice *dice) -{ - static struct snd_pcm_ops ops = { - .open = dice_open, - .close = dice_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dice_hw_params, - .hw_free = dice_hw_free, - .prepare = dice_prepare, - .trigger = dice_trigger, - .pointer = dice_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; - - return 0; -} - static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -978,7 +692,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice_card_strings(dice); - err = dice_create_pcm(dice); + err = snd_dice_create_pcm(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index ca4090d..4d9e55b 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -171,4 +171,6 @@ void snd_dice_stream_update(struct snd_dice *dice); int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice); +int snd_dice_create_pcm(struct snd_dice *dice); + #endif -- cgit v0.10.2 From 19af57b46dda93b34902739673d5f37d8c6d0d5f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:16 +0900 Subject: ALSA: dice: Split hwdep functionality into a file This commit adds a file and move some codes related to hwdep functionality. This interface is designed for mixer/control application. By using hwdep interface, the application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Additionally, this interface give a way to read Dice notification. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 1ddaeca..73b0e38 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,3 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \ + dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c new file mode 100644 index 0000000..a4dc02a --- /dev/null +++ b/sound/firewire/dice/dice-hwdep.c @@ -0,0 +1,190 @@ +/* + * dice_hwdep.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + struct snd_dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } else { + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min_t(long, count, sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; +} + +static int hwdep_get_info(struct snd_dice *dice, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_unlock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +int snd_dice_create_hwdep(struct snd_dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index b76ed06..dbc1239 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,186 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); -static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, - long count, loff_t *offset) -{ - struct snd_dice *dice = hwdep->private_data; - DEFINE_WAIT(wait); - union snd_firewire_event event; - - spin_lock_irq(&dice->lock); - - while (!dice->dev_lock_changed && dice->notification_bits == 0) { - prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irq(&dice->lock); - schedule(); - finish_wait(&dice->hwdep_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dice->lock); - } - - memset(&event, 0, sizeof(event)); - if (dice->dev_lock_changed) { - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = dice->dev_lock_count > 0; - dice->dev_lock_changed = false; - - count = min_t(long, count, sizeof(event.lock_status)); - } else { - event.dice_notification.type = - SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; - event.dice_notification.notification = dice->notification_bits; - dice->notification_bits = 0; - - count = min_t(long, count, sizeof(event.dice_notification)); - } - - spin_unlock_irq(&dice->lock); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; - - return count; -} - -static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, - poll_table *wait) -{ - struct snd_dice *dice = hwdep->private_data; - unsigned int events; - - poll_wait(file, &dice->hwdep_wait, wait); - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_changed || dice->notification_bits != 0) - events = POLLIN | POLLRDNORM; - else - events = 0; - spin_unlock_irq(&dice->lock); - - return events; -} - -static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) -{ - struct fw_device *dev = fw_parent_device(dice->unit); - struct snd_firewire_get_info info; - - memset(&info, 0, sizeof(info)); - info.type = SNDRV_FIREWIRE_TYPE_DICE; - info.card = dev->card->index; - *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); - *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); - strlcpy(info.device_name, dev_name(&dev->device), - sizeof(info.device_name)); - - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - -static int dice_hwdep_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == 0) { - dice->dev_lock_count = -1; - err = 0; - } else { - err = -EBUSY; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_unlock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == -1) { - dice->dev_lock_count = 0; - err = 0; - } else { - err = -EBADFD; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) -{ - struct snd_dice *dice = hwdep->private_data; - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_count == -1) - dice->dev_lock_count = 0; - spin_unlock_irq(&dice->lock); - - return 0; -} - -static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct snd_dice *dice = hwdep->private_data; - - switch (cmd) { - case SNDRV_FIREWIRE_IOCTL_GET_INFO: - return dice_hwdep_get_info(dice, (void __user *)arg); - case SNDRV_FIREWIRE_IOCTL_LOCK: - return dice_hwdep_lock(dice); - case SNDRV_FIREWIRE_IOCTL_UNLOCK: - return dice_hwdep_unlock(dice); - default: - return -ENOIOCTLCMD; - } -} - -#ifdef CONFIG_COMPAT -static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dice_hwdep_ioctl(hwdep, file, cmd, - (unsigned long)compat_ptr(arg)); -} -#else -#define dice_hwdep_compat_ioctl NULL -#endif - -static int dice_create_hwdep(struct snd_dice *dice) -{ - static const struct snd_hwdep_ops ops = { - .read = dice_hwdep_read, - .release = dice_hwdep_release, - .poll = dice_hwdep_poll, - .ioctl = dice_hwdep_ioctl, - .ioctl_compat = dice_hwdep_compat_ioctl, - }; - struct snd_hwdep *hwdep; - int err; - - err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); - if (err < 0) - return err; - strcpy(hwdep->name, "DICE"); - hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; - hwdep->ops = ops; - hwdep->private_data = dice; - hwdep->exclusive = true; - - return 0; -} - static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, unsigned int offset_q, unsigned int quadlets) { @@ -696,7 +516,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto error; - err = dice_create_hwdep(dice); + err = snd_dice_create_hwdep(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 4d9e55b..dcc8c78 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -173,4 +173,6 @@ void snd_dice_stream_lock_release(struct snd_dice *dice); int snd_dice_create_pcm(struct snd_dice *dice); +int snd_dice_create_hwdep(struct snd_dice *dice); + #endif -- cgit v0.10.2 From 04d426a039691bf114997a8af877682fdffcebd7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:17 +0900 Subject: ALSA: dice: Split proc interface into a file This commit adds a file and move some codes related to proc output. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 73b0e38..9a48289 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,3 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \ - dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-pcm.o \ + dice-hwdep.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c new file mode 100644 index 0000000..f5c1d1b --- /dev/null +++ b/sound/firewire/dice/dice-proc.c @@ -0,0 +1,252 @@ +/* + * dice_proc.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\\' && s[i + 1] == '\\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct snd_dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +void snd_dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index dbc1239..8e2c172 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,248 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); -static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, - unsigned int offset_q, unsigned int quadlets) -{ - unsigned int i; - int err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + 4 * offset_q, - buffer, 4 * quadlets, 0); - if (err < 0) - return err; - - for (i = 0; i < quadlets; ++i) - be32_to_cpus(&((u32 *)buffer)[i]); - - return 0; -} - -static const char *str_from_array(const char *const strs[], unsigned int count, - unsigned int i) -{ - if (i < count) - return strs[i]; - - return "(unknown)"; -} - -static void dice_proc_fixup_string(char *s, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < size; i += 4) - cpu_to_le32s((u32 *)(s + i)); - - for (i = 0; i < size - 2; ++i) { - if (s[i] == '\0') - return; - if (s[i] == '\\' && s[i + 1] == '\\') { - s[i + 2] = '\0'; - return; - } - } - s[size - 1] = '\0'; -} - -static void dice_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - static const char *const section_names[5] = { - "global", "tx", "rx", "ext_sync", "unused2" - }; - static const char *const clock_sources[] = { - "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", - "wc", "arx1", "arx2", "arx3", "arx4", "internal" - }; - static const char *const rates[] = { - "32000", "44100", "48000", "88200", "96000", "176400", "192000", - "any low", "any mid", "any high", "none" - }; - struct snd_dice *dice = entry->private_data; - u32 sections[ARRAY_SIZE(section_names) * 2]; - struct { - u32 number; - u32 size; - } tx_rx_header; - union { - struct { - u32 owner_hi, owner_lo; - u32 notification; - char nick_name[NICK_NAME_SIZE]; - u32 clock_select; - u32 enable; - u32 status; - u32 extended_status; - u32 sample_rate; - u32 version; - u32 clock_caps; - char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; - } global; - struct { - u32 iso; - u32 number_audio; - u32 number_midi; - u32 speed; - char names[TX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } tx; - struct { - u32 iso; - u32 seq_start; - u32 number_audio; - u32 number_midi; - char names[RX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } rx; - struct { - u32 clock_source; - u32 locked; - u32 rate; - u32 adat_user_data; - } ext_sync; - } buf; - unsigned int quadlets, stream, i; - - if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) - return; - snd_iprintf(buffer, "sections:\n"); - for (i = 0; i < ARRAY_SIZE(section_names); ++i) - snd_iprintf(buffer, " %s: offset %u, size %u\n", - section_names[i], - sections[i * 2], sections[i * 2 + 1]); - - quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); - if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) - return; - snd_iprintf(buffer, "global:\n"); - snd_iprintf(buffer, " owner: %04x:%04x%08x\n", - buf.global.owner_hi >> 16, - buf.global.owner_hi & 0xffff, buf.global.owner_lo); - snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); - dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); - snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); - snd_iprintf(buffer, " clock select: %s %s\n", - str_from_array(clock_sources, ARRAY_SIZE(clock_sources), - buf.global.clock_select & CLOCK_SOURCE_MASK), - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.clock_select & CLOCK_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " enable: %u\n", buf.global.enable); - snd_iprintf(buffer, " status: %slocked %s\n", - buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.status & - STATUS_NOMINAL_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); - snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); - snd_iprintf(buffer, " version: %u.%u.%u.%u\n", - (buf.global.version >> 24) & 0xff, - (buf.global.version >> 16) & 0xff, - (buf.global.version >> 8) & 0xff, - (buf.global.version >> 0) & 0xff); - if (quadlets >= 90) { - snd_iprintf(buffer, " clock caps:"); - for (i = 0; i <= 6; ++i) - if (buf.global.clock_caps & (1 << i)) - snd_iprintf(buffer, " %s", rates[i]); - for (i = 0; i <= 12; ++i) - if (buf.global.clock_caps & (1 << (16 + i))) - snd_iprintf(buffer, " %s", clock_sources[i]); - snd_iprintf(buffer, "\n"); - dice_proc_fixup_string(buf.global.clock_source_names, - CLOCK_SOURCE_NAMES_SIZE); - snd_iprintf(buffer, " clock source names: %s\n", - buf.global.clock_source_names); - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "tx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); - snd_iprintf(buffer, " audio channels: %u\n", - buf.tx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); - snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.tx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.tx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.tx.ac3_enable); - } - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "rx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); - snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); - snd_iprintf(buffer, " audio channels: %u\n", - buf.rx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.rx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.rx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.rx.ac3_enable); - } - } - - quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); - if (quadlets >= 4) { - if (dice_proc_read_mem(dice, &buf.ext_sync, - sections[6], 4) < 0) - return; - snd_iprintf(buffer, "ext status:\n"); - snd_iprintf(buffer, " clock source: %s\n", - str_from_array(clock_sources, - ARRAY_SIZE(clock_sources), - buf.ext_sync.clock_source)); - snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); - snd_iprintf(buffer, " rate: %s\n", - str_from_array(rates, ARRAY_SIZE(rates), - buf.ext_sync.rate)); - snd_iprintf(buffer, " adat user data: "); - if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) - snd_iprintf(buffer, "-\n"); - else - snd_iprintf(buffer, "%x\n", - buf.ext_sync.adat_user_data); - } -} - -static void dice_create_proc(struct snd_dice *dice) -{ - struct snd_info_entry *entry; - - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); -} - #define OUI_WEISS 0x001c6a #define DICE_CATEGORY_ID 0x04 @@ -520,7 +278,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto error; - dice_create_proc(dice); + snd_dice_create_proc(dice); err = snd_dice_stream_init(dice); if (err < 0) diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index dcc8c78..969189a 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -175,4 +175,6 @@ int snd_dice_create_pcm(struct snd_dice *dice); int snd_dice_create_hwdep(struct snd_dice *dice); +void snd_dice_create_proc(struct snd_dice *dice); + #endif -- cgit v0.10.2 From 2c2416c83e7345fc51af49132c40ec7d337d4132 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:18 +0900 Subject: ALSA: dice: Add new functions for constraints of PCM parameters This commit adds a new functions and some arrangement for PCM restriction. This arrangement is due to the number of channels which each Dice device has. I note that minimum number for period becomes 2, instead of 1 because its PCM functionality has SNDRV_PCM_INFO_BATCH, this means that the driver uses double (or more) buffering so the minimum number for period should be 2. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index deacb53..2e531bd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -67,71 +67,92 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, return snd_interval_refine(c, &channels); } - -static int pcm_open(struct snd_pcm_substream *substream) +static void limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + unsigned int *pcm_channels) { - 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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; + struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int i, rate, mode; - err = snd_dice_stream_lock_try(dice); - if (err < 0) - goto error; - - runtime->hw = hardware; + hw->channels_min = UINT_MAX; + hw->channels_max = 0; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + hw->rates |= snd_pcm_rate_to_rate_bit(rate); + + if (pcm_channels[mode] == 0) + continue; + hw->channels_min = min(hw->channels_min, pcm_channels[mode]); + hw->channels_max = max(hw->channels_max, pcm_channels[mode]); } + snd_pcm_limit_hw_rates(runtime); +} - for (i = 0; i < 3; ++i) { - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } - } +static void limit_period_and_buffer(struct snd_pcm_hardware *hw) +{ + hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ + hw->periods_max = UINT_MAX; + + hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ + + /* Just to prevent from allocating much pages. */ + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; +} + +static int init_hw_info(struct snd_dice *dice, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; + int err; + + hw->info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER; + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + + limit_channels_and_rates(dice, runtime, dice->rx_channels); + limit_period_and_buffer(hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, dice_rate_constraint, dice, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) - goto err_lock; + goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, dice_channels_constraint, dice, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) - goto err_lock; + goto end; err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); - if (err < 0) - goto err_lock; +end: + return err; +} - return 0; +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; -err_lock: + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto end; + + err = init_hw_info(dice, substream); + if (err < 0) + goto err_locked; +end: + return err; +err_locked: snd_dice_stream_lock_release(dice); -error: return err; } -- cgit v0.10.2 From 60cea522963fc0b3893d78e568666d105f117ff9 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:24 +0900 Subject: ALSA: dice: remove experimental state Some developers test this driver, thus it's better to remove its experimental state. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 46dff64..a433149 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -13,12 +13,12 @@ config SND_FIREWIRE_LIB select SND_RAWMIDI config SND_DICE - tristate "DICE-based DACs (EXPERIMENTAL)" + tristate "DICE-based DACs support" select SND_HWDEP select SND_FIREWIRE_LIB help Say Y here to include support for many DACs based on the DICE - chip family (DICE-II/Jr/Mini) from TC Applied Technologies. + chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces. At the moment, this driver supports playback only. If you want to use devices that support capturing, use FFADO instead. -- cgit v0.10.2 From 8832c5a74ba3506c51b6637ac78941fcd21afbef Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:25 +0900 Subject: ALSA: speakers: Rename to oxfw and rename some members This commit renames 'firewire-speakers' to 'oxfw' to enhance support for devices which based on OXFW970/971. A line for MODULE_ALIAS is added. Additionally, to help for works in followed paches, some members in private structure are renamed. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index a433149..2a5b9a6 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -26,15 +26,17 @@ config SND_DICE To compile this driver as a module, choose M here: the module will be called snd-dice. -config SND_FIREWIRE_SPEAKERS - tristate "FireWire speakers" +config SND_OXFW + tristate "Oxford Semiconductor FW970/971 chipset support" select SND_FIREWIRE_LIB help - Say Y here to include support for the Griffin FireWave Surround - and the LaCie FireWire Speakers. + Say Y here to include support for FireWire devices based on + Oxford Semiconductor FW970/971 chipset. + * Griffin Firewave + * LaCie Firewire Speakers To compile this driver as a module, choose M here: the module - will be called snd-firewire-speakers. + will be called snd-oxfw. config SND_ISIGHT tristate "Apple iSight microphone" diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index c50761c..5ed6fb7 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,12 +1,12 @@ 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-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o obj-$(CONFIG_SND_DICE) += dice/ -obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o +obj-$(CONFIG_SND_OXFW) += snd-oxfw.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ diff --git a/sound/firewire/oxfw.c b/sound/firewire/oxfw.c new file mode 100644 index 0000000..b7498e9 --- /dev/null +++ b/sound/firewire/oxfw.c @@ -0,0 +1,793 @@ +/* + * oxfw.c - a part of driver for OXFW970/971 based devices + * + * 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 "cmp.h" +#include "fcp.h" +#include "amdtp.h" +#include "lib.h" + +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ + +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 + +#define VENDOR_GRIFFIN 0x001292 +#define VENDOR_LACIE 0x00d04b + +#define SPECIFIER_1394TA 0x00a02d +#define VERSION_AVC 0x010001 + +struct device_info { + const char *driver_name; + const char *short_name; + const char *long_name; + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +struct snd_oxfw { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct mutex mutex; + struct cmp_connection in_conn; + struct amdtp_stream rx_stream; + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; +}; + +MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("snd-firewire-speakers"); + +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static unsigned int stereo_rates[] = { 48000, 96000 }; + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + /* two channels work only at 48/96 kHz */ + if (snd_interval_max(channels) < 6) + return snd_interval_list(rate, 2, stereo_rates, 0); + return 0; +} + +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + /* 32/44.1 kHz work only with all six channels */ + if (snd_interval_max(rate) < 48000) + return snd_interval_refine(channels, &all_channels); + return 0; +} + +static int firewave_constraints(struct snd_pcm_runtime *runtime) +{ + static unsigned int channels_list[] = { 2, 6 }; + static struct snd_pcm_hw_constraint_list channels_list_constraint = { + .count = 2, + .list = channels_list, + }; + int err; + + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000; + runtime->hw.channels_max = 6; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &channels_list_constraint); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + firewave_rate_constraint, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + firewave_channels_constraint, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + return 0; +} + +static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) +{ + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + + return 0; +} + +static int oxfw_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 = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->hw = hardware; + + err = oxfw->device_info->pcm_constraints(runtime); + if (err < 0) + return err; + err = snd_pcm_limit_hw_rates(runtime); + if (err < 0) + return err; + + err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); + if (err < 0) + return err; + + return 0; +} + +static int oxfw_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void oxfw_stop_stream(struct snd_oxfw *oxfw) +{ + if (amdtp_stream_running(&oxfw->rx_stream)) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + } +} + +static int oxfw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_stream_set_parameters(&oxfw->rx_stream, + params_rate(hw_params), + params_channels(hw_params), + 0); + + amdtp_stream_set_pcm_format(&oxfw->rx_stream, + params_format(hw_params)); + + err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params), + AVC_GENERAL_PLUG_DIR_IN, 0); + if (err < 0) { + dev_err(&oxfw->unit->device, "failed to set sample rate\n"); + goto err_buffer; + } + + return 0; + +err_buffer: + snd_pcm_lib_free_vmalloc_buffer(substream); +error: + return err; +} + +static int oxfw_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int oxfw_prepare(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + + if (amdtp_streaming_error(&oxfw->rx_stream)) + oxfw_stop_stream(oxfw); + + if (!amdtp_stream_running(&oxfw->rx_stream)) { + err = cmp_connection_establish(&oxfw->in_conn, + amdtp_stream_get_max_payload(&oxfw->rx_stream)); + if (err < 0) + goto err_mutex; + + err = amdtp_stream_start(&oxfw->rx_stream, + oxfw->in_conn.resources.channel, + oxfw->in_conn.speed); + if (err < 0) + goto err_connection; + } + + mutex_unlock(&oxfw->mutex); + + amdtp_stream_pcm_prepare(&oxfw->rx_stream); + + return 0; + +err_connection: + cmp_connection_break(&oxfw->in_conn); +err_mutex: + mutex_unlock(&oxfw->mutex); + + return err; +} + +static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); + return 0; +} + +static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + return amdtp_stream_pcm_pointer(&oxfw->rx_stream); +} + +static int oxfw_create_pcm(struct snd_oxfw *oxfw) +{ + static struct snd_pcm_ops ops = { + .open = oxfw_open, + .close = oxfw_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxfw_hw_params, + .hw_free = oxfw_hw_free, + .prepare = oxfw_prepare, + .trigger = oxfw_trigger, + .pointer = oxfw_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = oxfw; + strcpy(pcm->name, oxfw->device_info->short_name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); + return 0; +} + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + + value->value.integer.value[0] = !oxfw->mute; + + return 0; +} + +static int oxfw_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == oxfw->mute) + return 0; + + err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); + if (err < 0) + return err; + oxfw->mute = mute; + + return 1; +} + +static int oxfw_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct snd_oxfw *oxfw = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = oxfw->device_info->mixer_channels; + info->value.integer.min = oxfw->volume_min; + info->value.integer.max = oxfw->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int oxfw_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = oxfw->volume[i]; + + return 0; +} + +static int oxfw_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < oxfw->volume_min || + value->value.integer.value[i] > oxfw->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + oxfw->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = oxfw_volume_command(oxfw, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + oxfw->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +static int oxfw_create_mixer(struct snd_oxfw *oxfw) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = oxfw_mute_get, + .put = oxfw_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = oxfw_volume_info, + .get = oxfw_volume_get, + .put = oxfw_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = oxfw_volume_command(oxfw, &oxfw->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = oxfw_volume_command(oxfw, &oxfw->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + err = oxfw_volume_command(oxfw, &oxfw->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(oxfw->card, + snd_ctl_new1(&controls[i], oxfw)); + if (err < 0) + return err; + } + + return 0; +} + +static u32 oxfw_read_firmware_version(struct fw_unit *unit) +{ + __be32 data; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); + return err >= 0 ? be32_to_cpu(data) : 0; +} + +static void oxfw_card_free(struct snd_card *card) +{ + struct snd_oxfw *oxfw = card->private_data; + + amdtp_stream_destroy(&oxfw->rx_stream); + cmp_connection_destroy(&oxfw->in_conn); + fw_unit_put(oxfw->unit); + mutex_destroy(&oxfw->mutex); +} + +static int oxfw_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) +{ + struct fw_device *fw_dev = fw_parent_device(unit); + struct snd_card *card; + struct snd_oxfw *oxfw; + u32 firmware; + int err; + + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(*oxfw), &card); + if (err < 0) + return err; + + oxfw = card->private_data; + oxfw->card = card; + mutex_init(&oxfw->mutex); + oxfw->unit = fw_unit_get(unit); + oxfw->device_info = (const struct device_info *)id->driver_data; + + err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0); + if (err < 0) + goto err_unit; + + err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM, + CIP_NONBLOCKING); + if (err < 0) + goto err_connection; + + card->private_free = oxfw_card_free; + + strcpy(card->driver, oxfw->device_info->driver_name); + strcpy(card->shortname, oxfw->device_info->short_name); + firmware = oxfw_read_firmware_version(unit); + snprintf(card->longname, sizeof(card->longname), + "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + oxfw->device_info->long_name, + firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&unit->device), 100 << fw_dev->max_speed); + strcpy(card->mixername, "OXFW"); + + err = oxfw_create_pcm(oxfw); + if (err < 0) + goto error; + + err = oxfw_create_mixer(oxfw); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, oxfw); + + return 0; + +err_connection: + cmp_connection_destroy(&oxfw->in_conn); +err_unit: + fw_unit_put(oxfw->unit); + mutex_destroy(&oxfw->mutex); +error: + snd_card_free(card); + return err; +} + +static void oxfw_bus_reset(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + fcp_bus_reset(oxfw->unit); + + if (cmp_connection_update(&oxfw->in_conn) < 0) { + amdtp_stream_pcm_abort(&oxfw->rx_stream); + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + return; + } + + amdtp_stream_update(&oxfw->rx_stream); +} + +static void oxfw_remove(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + amdtp_stream_pcm_abort(&oxfw->rx_stream); + snd_card_disconnect(oxfw->card); + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + snd_card_free_when_closed(oxfw->card); +} + +static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .short_name = "FireWave", + .long_name = "Griffin FireWave Surround", + .pcm_constraints = firewave_constraints, + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, +}; + +static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .short_name = "FireWire Speakers", + .long_name = "LaCie FireWire Speakers", + .pcm_constraints = lacie_speakers_constraints, + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, +}; + +static const struct ieee1394_device_id oxfw_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_GRIFFIN, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&griffin_firewave, + }, + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LACIE, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&lacie_speakers, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); + +static struct fw_driver oxfw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = oxfw_probe, + .update = oxfw_bus_reset, + .remove = oxfw_remove, + .id_table = oxfw_id_table, +}; + +static int __init snd_oxfw_init(void) +{ + return driver_register(&oxfw_driver.driver); +} + +static void __exit snd_oxfw_exit(void) +{ + driver_unregister(&oxfw_driver.driver); +} + +module_init(snd_oxfw_init); +module_exit(snd_oxfw_exit); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c deleted file mode 100644 index 768d40d..0000000 --- a/sound/firewire/speakers.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * OXFW970-based speakers 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 "cmp.h" -#include "fcp.h" -#include "amdtp.h" -#include "lib.h" - -#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) -/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ - -#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) -#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 -#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 - -#define VENDOR_GRIFFIN 0x001292 -#define VENDOR_LACIE 0x00d04b - -#define SPECIFIER_1394TA 0x00a02d -#define VERSION_AVC 0x010001 - -struct device_info { - const char *driver_name; - const char *short_name; - const char *long_name; - int (*pcm_constraints)(struct snd_pcm_runtime *runtime); - unsigned int mixer_channels; - u8 mute_fb_id; - u8 volume_fb_id; -}; - -struct fwspk { - struct snd_card *card; - struct fw_unit *unit; - const struct device_info *device_info; - struct mutex mutex; - struct cmp_connection connection; - struct amdtp_stream stream; - bool mute; - s16 volume[6]; - s16 volume_min; - s16 volume_max; -}; - -MODULE_DESCRIPTION("FireWire speakers driver"); -MODULE_AUTHOR("Clemens Ladisch "); -MODULE_LICENSE("GPL v2"); - -static int firewave_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static unsigned int stereo_rates[] = { 48000, 96000 }; - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - - /* two channels work only at 48/96 kHz */ - if (snd_interval_max(channels) < 6) - return snd_interval_list(rate, 2, stereo_rates, 0); - return 0; -} - -static int firewave_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static const struct snd_interval all_channels = { .min = 6, .max = 6 }; - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - /* 32/44.1 kHz work only with all six channels */ - if (snd_interval_max(rate) < 48000) - return snd_interval_refine(channels, &all_channels); - return 0; -} - -static int firewave_constraints(struct snd_pcm_runtime *runtime) -{ - static unsigned int channels_list[] = { 2, 6 }; - static struct snd_pcm_hw_constraint_list channels_list_constraint = { - .count = 2, - .list = channels_list, - }; - int err; - - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000; - runtime->hw.channels_max = 6; - - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &channels_list_constraint); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - firewave_rate_constraint, NULL, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - firewave_channels_constraint, NULL, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - - return 0; -} - -static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) -{ - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000; - - return 0; -} - -static int fwspk_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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 4 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct fwspk *fwspk = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - runtime->hw = hardware; - - err = fwspk->device_info->pcm_constraints(runtime); - if (err < 0) - return err; - err = snd_pcm_limit_hw_rates(runtime); - if (err < 0) - return err; - - err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime); - if (err < 0) - return err; - - return 0; -} - -static int fwspk_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -static void fwspk_stop_stream(struct fwspk *fwspk) -{ - if (amdtp_stream_running(&fwspk->stream)) { - amdtp_stream_stop(&fwspk->stream); - cmp_connection_break(&fwspk->connection); - } -} - -static int fwspk_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct fwspk *fwspk = substream->private_data; - int err; - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - goto error; - - amdtp_stream_set_parameters(&fwspk->stream, - params_rate(hw_params), - params_channels(hw_params), - 0); - - amdtp_stream_set_pcm_format(&fwspk->stream, - params_format(hw_params)); - - err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params), - AVC_GENERAL_PLUG_DIR_IN, 0); - if (err < 0) { - dev_err(&fwspk->unit->device, "failed to set sample rate\n"); - goto err_buffer; - } - - return 0; - -err_buffer: - snd_pcm_lib_free_vmalloc_buffer(substream); -error: - return err; -} - -static int fwspk_hw_free(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int fwspk_prepare(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - int err; - - mutex_lock(&fwspk->mutex); - - if (amdtp_streaming_error(&fwspk->stream)) - fwspk_stop_stream(fwspk); - - if (!amdtp_stream_running(&fwspk->stream)) { - err = cmp_connection_establish(&fwspk->connection, - amdtp_stream_get_max_payload(&fwspk->stream)); - if (err < 0) - goto err_mutex; - - err = amdtp_stream_start(&fwspk->stream, - fwspk->connection.resources.channel, - fwspk->connection.speed); - if (err < 0) - goto err_connection; - } - - mutex_unlock(&fwspk->mutex); - - amdtp_stream_pcm_prepare(&fwspk->stream); - - return 0; - -err_connection: - cmp_connection_break(&fwspk->connection); -err_mutex: - mutex_unlock(&fwspk->mutex); - - return err; -} - -static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct fwspk *fwspk = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&fwspk->stream, pcm); - return 0; -} - -static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - - return amdtp_stream_pcm_pointer(&fwspk->stream); -} - -static int fwspk_create_pcm(struct fwspk *fwspk) -{ - static struct snd_pcm_ops ops = { - .open = fwspk_open, - .close = fwspk_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = fwspk_hw_params, - .hw_free = fwspk_hw_free, - .prepare = fwspk_prepare, - .trigger = fwspk_trigger, - .pointer = fwspk_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = fwspk; - strcpy(pcm->name, fwspk->device_info->short_name); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); - return 0; -} - -enum control_action { CTL_READ, CTL_WRITE }; -enum control_attribute { - CTL_MIN = 0x02, - CTL_MAX = 0x03, - CTL_CURRENT = 0x10, -}; - -static int fwspk_mute_command(struct fwspk *fwspk, bool *value, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(11, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ - buf[5] = 0x10; /* control attribute: current */ - buf[6] = 0x02; /* selector length */ - buf[7] = 0x00; /* audio channel number */ - buf[8] = 0x01; /* control selector: mute */ - buf[9] = 0x01; /* control data length */ - if (action == CTL_READ) - buf[10] = 0xff; - else - buf[10] = *value ? 0x70 : 0x60; - - err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); - if (err < 0) - goto error; - if (err < 11) { - dev_err(&fwspk->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&fwspk->unit->device, "mute command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = buf[10] == 0x70; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, - unsigned int channel, - enum control_attribute attribute, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(12, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ - buf[5] = attribute; /* control attribute */ - buf[6] = 0x02; /* selector length */ - buf[7] = channel; /* audio channel number */ - buf[8] = 0x02; /* control selector: volume */ - buf[9] = 0x02; /* control data length */ - if (action == CTL_READ) { - buf[10] = 0xff; - buf[11] = 0xff; - } else { - buf[10] = *value >> 8; - buf[11] = *value; - } - - err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); - if (err < 0) - goto error; - if (err < 12) { - dev_err(&fwspk->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&fwspk->unit->device, "volume command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = (buf[10] << 8) | buf[11]; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int fwspk_mute_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - - value->value.integer.value[0] = !fwspk->mute; - - return 0; -} - -static int fwspk_mute_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - bool mute; - int err; - - mute = !value->value.integer.value[0]; - - if (mute == fwspk->mute) - return 0; - - err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); - if (err < 0) - return err; - fwspk->mute = mute; - - return 1; -} - -static int fwspk_volume_info(struct snd_kcontrol *control, - struct snd_ctl_elem_info *info) -{ - struct fwspk *fwspk = control->private_data; - - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = fwspk->device_info->mixer_channels; - info->value.integer.min = fwspk->volume_min; - info->value.integer.max = fwspk->volume_max; - - return 0; -} - -static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; - -static int fwspk_volume_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - unsigned int i; - - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) - value->value.integer.value[channel_map[i]] = fwspk->volume[i]; - - return 0; -} - -static int fwspk_volume_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - unsigned int i, changed_channels; - bool equal_values = true; - s16 volume; - int err; - - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { - if (value->value.integer.value[i] < fwspk->volume_min || - value->value.integer.value[i] > fwspk->volume_max) - return -EINVAL; - if (value->value.integer.value[i] != - value->value.integer.value[0]) - equal_values = false; - } - - changed_channels = 0; - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) - if (value->value.integer.value[channel_map[i]] != - fwspk->volume[i]) - changed_channels |= 1 << (i + 1); - - if (equal_values && changed_channels != 0) - changed_channels = 1 << 0; - - for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { - volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; - if (changed_channels & (1 << i)) { - err = fwspk_volume_command(fwspk, &volume, i, - CTL_CURRENT, CTL_WRITE); - if (err < 0) - return err; - } - if (i > 0) - fwspk->volume[i - 1] = volume; - } - - return changed_channels != 0; -} - -static int fwspk_create_mixer(struct fwspk *fwspk) -{ - static const struct snd_kcontrol_new controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = fwspk_mute_get, - .put = fwspk_mute_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .info = fwspk_volume_info, - .get = fwspk_volume_get, - .put = fwspk_volume_put, - }, - }; - unsigned int i, first_ch; - int err; - - err = fwspk_volume_command(fwspk, &fwspk->volume_min, - 0, CTL_MIN, CTL_READ); - if (err < 0) - return err; - err = fwspk_volume_command(fwspk, &fwspk->volume_max, - 0, CTL_MAX, CTL_READ); - if (err < 0) - return err; - - err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); - if (err < 0) - return err; - - first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { - err = fwspk_volume_command(fwspk, &fwspk->volume[i], - first_ch + i, CTL_CURRENT, CTL_READ); - if (err < 0) - return err; - } - - for (i = 0; i < ARRAY_SIZE(controls); ++i) { - err = snd_ctl_add(fwspk->card, - snd_ctl_new1(&controls[i], fwspk)); - if (err < 0) - return err; - } - - return 0; -} - -static u32 fwspk_read_firmware_version(struct fw_unit *unit) -{ - __be32 data; - int err; - - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); - return err >= 0 ? be32_to_cpu(data) : 0; -} - -static void fwspk_card_free(struct snd_card *card) -{ - struct fwspk *fwspk = card->private_data; - - amdtp_stream_destroy(&fwspk->stream); - cmp_connection_destroy(&fwspk->connection); - fw_unit_put(fwspk->unit); - mutex_destroy(&fwspk->mutex); -} - -static int fwspk_probe(struct fw_unit *unit, - const struct ieee1394_device_id *id) -{ - struct fw_device *fw_dev = fw_parent_device(unit); - struct snd_card *card; - struct fwspk *fwspk; - u32 firmware; - int err; - - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*fwspk), &card); - if (err < 0) - return err; - - fwspk = card->private_data; - fwspk->card = card; - mutex_init(&fwspk->mutex); - fwspk->unit = fw_unit_get(unit); - fwspk->device_info = (const struct device_info *)id->driver_data; - - err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0); - if (err < 0) - goto err_unit; - - err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM, - CIP_NONBLOCKING); - if (err < 0) - goto err_connection; - - card->private_free = fwspk_card_free; - - strcpy(card->driver, fwspk->device_info->driver_name); - strcpy(card->shortname, fwspk->device_info->short_name); - firmware = fwspk_read_firmware_version(unit); - snprintf(card->longname, sizeof(card->longname), - "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", - fwspk->device_info->long_name, - firmware >> 20, firmware & 0xffff, - fw_dev->config_rom[3], fw_dev->config_rom[4], - dev_name(&unit->device), 100 << fw_dev->max_speed); - strcpy(card->mixername, "OXFW970"); - - err = fwspk_create_pcm(fwspk); - if (err < 0) - goto error; - - err = fwspk_create_mixer(fwspk); - if (err < 0) - goto error; - - err = snd_card_register(card); - if (err < 0) - goto error; - - dev_set_drvdata(&unit->device, fwspk); - - return 0; - -err_connection: - cmp_connection_destroy(&fwspk->connection); -err_unit: - fw_unit_put(fwspk->unit); - mutex_destroy(&fwspk->mutex); -error: - snd_card_free(card); - return err; -} - -static void fwspk_bus_reset(struct fw_unit *unit) -{ - struct fwspk *fwspk = dev_get_drvdata(&unit->device); - - fcp_bus_reset(fwspk->unit); - - if (cmp_connection_update(&fwspk->connection) < 0) { - amdtp_stream_pcm_abort(&fwspk->stream); - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - return; - } - - amdtp_stream_update(&fwspk->stream); -} - -static void fwspk_remove(struct fw_unit *unit) -{ - struct fwspk *fwspk = dev_get_drvdata(&unit->device); - - amdtp_stream_pcm_abort(&fwspk->stream); - snd_card_disconnect(fwspk->card); - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - snd_card_free_when_closed(fwspk->card); -} - -static const struct device_info griffin_firewave = { - .driver_name = "FireWave", - .short_name = "FireWave", - .long_name = "Griffin FireWave Surround", - .pcm_constraints = firewave_constraints, - .mixer_channels = 6, - .mute_fb_id = 0x01, - .volume_fb_id = 0x02, -}; - -static const struct device_info lacie_speakers = { - .driver_name = "FWSpeakers", - .short_name = "FireWire Speakers", - .long_name = "LaCie FireWire Speakers", - .pcm_constraints = lacie_speakers_constraints, - .mixer_channels = 1, - .mute_fb_id = 0x01, - .volume_fb_id = 0x01, -}; - -static const struct ieee1394_device_id fwspk_id_table[] = { - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_GRIFFIN, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&griffin_firewave, - }, - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_LACIE, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&lacie_speakers, - }, - { } -}; -MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); - -static struct fw_driver fwspk_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - .bus = &fw_bus_type, - }, - .probe = fwspk_probe, - .update = fwspk_bus_reset, - .remove = fwspk_remove, - .id_table = fwspk_id_table, -}; - -static int __init alsa_fwspk_init(void) -{ - return driver_register(&fwspk_driver.driver); -} - -static void __exit alsa_fwspk_exit(void) -{ - driver_unregister(&fwspk_driver.driver); -} - -module_init(alsa_fwspk_init); -module_exit(alsa_fwspk_exit); -- cgit v0.10.2 From 1a4e39c2e5ca2eb494a53ecd73055562f690bca0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:26 +0900 Subject: ALSA: oxfw: Move to its own directory Followed commits add much codes. To make the work easy, this commit creates own directory and move current file to it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 5ed6fb7..8b37f08 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -6,7 +6,7 @@ snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o obj-$(CONFIG_SND_DICE) += dice/ -obj-$(CONFIG_SND_OXFW) += snd-oxfw.o +obj-$(CONFIG_SND_OXFW) += oxfw/ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ diff --git a/sound/firewire/oxfw.c b/sound/firewire/oxfw.c deleted file mode 100644 index b7498e9..0000000 --- a/sound/firewire/oxfw.c +++ /dev/null @@ -1,793 +0,0 @@ -/* - * oxfw.c - a part of driver for OXFW970/971 based devices - * - * 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 "cmp.h" -#include "fcp.h" -#include "amdtp.h" -#include "lib.h" - -#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) -/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ - -#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) -#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 -#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 - -#define VENDOR_GRIFFIN 0x001292 -#define VENDOR_LACIE 0x00d04b - -#define SPECIFIER_1394TA 0x00a02d -#define VERSION_AVC 0x010001 - -struct device_info { - const char *driver_name; - const char *short_name; - const char *long_name; - int (*pcm_constraints)(struct snd_pcm_runtime *runtime); - unsigned int mixer_channels; - u8 mute_fb_id; - u8 volume_fb_id; -}; - -struct snd_oxfw { - struct snd_card *card; - struct fw_unit *unit; - const struct device_info *device_info; - struct mutex mutex; - struct cmp_connection in_conn; - struct amdtp_stream rx_stream; - bool mute; - s16 volume[6]; - s16 volume_min; - s16 volume_max; -}; - -MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); -MODULE_AUTHOR("Clemens Ladisch "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("snd-firewire-speakers"); - -static int firewave_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static unsigned int stereo_rates[] = { 48000, 96000 }; - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - - /* two channels work only at 48/96 kHz */ - if (snd_interval_max(channels) < 6) - return snd_interval_list(rate, 2, stereo_rates, 0); - return 0; -} - -static int firewave_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static const struct snd_interval all_channels = { .min = 6, .max = 6 }; - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - /* 32/44.1 kHz work only with all six channels */ - if (snd_interval_max(rate) < 48000) - return snd_interval_refine(channels, &all_channels); - return 0; -} - -static int firewave_constraints(struct snd_pcm_runtime *runtime) -{ - static unsigned int channels_list[] = { 2, 6 }; - static struct snd_pcm_hw_constraint_list channels_list_constraint = { - .count = 2, - .list = channels_list, - }; - int err; - - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000; - runtime->hw.channels_max = 6; - - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &channels_list_constraint); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - firewave_rate_constraint, NULL, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - firewave_channels_constraint, NULL, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - - return 0; -} - -static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) -{ - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000; - - return 0; -} - -static int oxfw_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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 4 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - runtime->hw = hardware; - - err = oxfw->device_info->pcm_constraints(runtime); - if (err < 0) - return err; - err = snd_pcm_limit_hw_rates(runtime); - if (err < 0) - return err; - - err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); - if (err < 0) - return err; - - return 0; -} - -static int oxfw_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -static void oxfw_stop_stream(struct snd_oxfw *oxfw) -{ - if (amdtp_stream_running(&oxfw->rx_stream)) { - amdtp_stream_stop(&oxfw->rx_stream); - cmp_connection_break(&oxfw->in_conn); - } -} - -static int oxfw_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_oxfw *oxfw = substream->private_data; - int err; - - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - goto error; - - amdtp_stream_set_parameters(&oxfw->rx_stream, - params_rate(hw_params), - params_channels(hw_params), - 0); - - amdtp_stream_set_pcm_format(&oxfw->rx_stream, - params_format(hw_params)); - - err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params), - AVC_GENERAL_PLUG_DIR_IN, 0); - if (err < 0) { - dev_err(&oxfw->unit->device, "failed to set sample rate\n"); - goto err_buffer; - } - - return 0; - -err_buffer: - snd_pcm_lib_free_vmalloc_buffer(substream); -error: - return err; -} - -static int oxfw_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int oxfw_prepare(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - int err; - - mutex_lock(&oxfw->mutex); - - if (amdtp_streaming_error(&oxfw->rx_stream)) - oxfw_stop_stream(oxfw); - - if (!amdtp_stream_running(&oxfw->rx_stream)) { - err = cmp_connection_establish(&oxfw->in_conn, - amdtp_stream_get_max_payload(&oxfw->rx_stream)); - if (err < 0) - goto err_mutex; - - err = amdtp_stream_start(&oxfw->rx_stream, - oxfw->in_conn.resources.channel, - oxfw->in_conn.speed); - if (err < 0) - goto err_connection; - } - - mutex_unlock(&oxfw->mutex); - - amdtp_stream_pcm_prepare(&oxfw->rx_stream); - - return 0; - -err_connection: - cmp_connection_break(&oxfw->in_conn); -err_mutex: - mutex_unlock(&oxfw->mutex); - - return err; -} - -static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); - return 0; -} - -static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - - return amdtp_stream_pcm_pointer(&oxfw->rx_stream); -} - -static int oxfw_create_pcm(struct snd_oxfw *oxfw) -{ - static struct snd_pcm_ops ops = { - .open = oxfw_open, - .close = oxfw_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = oxfw_hw_params, - .hw_free = oxfw_hw_free, - .prepare = oxfw_prepare, - .trigger = oxfw_trigger, - .pointer = oxfw_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = oxfw; - strcpy(pcm->name, oxfw->device_info->short_name); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); - return 0; -} - -enum control_action { CTL_READ, CTL_WRITE }; -enum control_attribute { - CTL_MIN = 0x02, - CTL_MAX = 0x03, - CTL_CURRENT = 0x10, -}; - -static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(11, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ - buf[5] = 0x10; /* control attribute: current */ - buf[6] = 0x02; /* selector length */ - buf[7] = 0x00; /* audio channel number */ - buf[8] = 0x01; /* control selector: mute */ - buf[9] = 0x01; /* control data length */ - if (action == CTL_READ) - buf[10] = 0xff; - else - buf[10] = *value ? 0x70 : 0x60; - - err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); - if (err < 0) - goto error; - if (err < 11) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "mute command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = buf[10] == 0x70; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, - unsigned int channel, - enum control_attribute attribute, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(12, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ - buf[5] = attribute; /* control attribute */ - buf[6] = 0x02; /* selector length */ - buf[7] = channel; /* audio channel number */ - buf[8] = 0x02; /* control selector: volume */ - buf[9] = 0x02; /* control data length */ - if (action == CTL_READ) { - buf[10] = 0xff; - buf[11] = 0xff; - } else { - buf[10] = *value >> 8; - buf[11] = *value; - } - - err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); - if (err < 0) - goto error; - if (err < 12) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "volume command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = (buf[10] << 8) | buf[11]; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_mute_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - - value->value.integer.value[0] = !oxfw->mute; - - return 0; -} - -static int oxfw_mute_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - bool mute; - int err; - - mute = !value->value.integer.value[0]; - - if (mute == oxfw->mute) - return 0; - - err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); - if (err < 0) - return err; - oxfw->mute = mute; - - return 1; -} - -static int oxfw_volume_info(struct snd_kcontrol *control, - struct snd_ctl_elem_info *info) -{ - struct snd_oxfw *oxfw = control->private_data; - - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = oxfw->device_info->mixer_channels; - info->value.integer.min = oxfw->volume_min; - info->value.integer.max = oxfw->volume_max; - - return 0; -} - -static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; - -static int oxfw_volume_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - unsigned int i; - - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - value->value.integer.value[channel_map[i]] = oxfw->volume[i]; - - return 0; -} - -static int oxfw_volume_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - unsigned int i, changed_channels; - bool equal_values = true; - s16 volume; - int err; - - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { - if (value->value.integer.value[i] < oxfw->volume_min || - value->value.integer.value[i] > oxfw->volume_max) - return -EINVAL; - if (value->value.integer.value[i] != - value->value.integer.value[0]) - equal_values = false; - } - - changed_channels = 0; - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - if (value->value.integer.value[channel_map[i]] != - oxfw->volume[i]) - changed_channels |= 1 << (i + 1); - - if (equal_values && changed_channels != 0) - changed_channels = 1 << 0; - - for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { - volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; - if (changed_channels & (1 << i)) { - err = oxfw_volume_command(oxfw, &volume, i, - CTL_CURRENT, CTL_WRITE); - if (err < 0) - return err; - } - if (i > 0) - oxfw->volume[i - 1] = volume; - } - - return changed_channels != 0; -} - -static int oxfw_create_mixer(struct snd_oxfw *oxfw) -{ - static const struct snd_kcontrol_new controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = oxfw_mute_get, - .put = oxfw_mute_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .info = oxfw_volume_info, - .get = oxfw_volume_get, - .put = oxfw_volume_put, - }, - }; - unsigned int i, first_ch; - int err; - - err = oxfw_volume_command(oxfw, &oxfw->volume_min, - 0, CTL_MIN, CTL_READ); - if (err < 0) - return err; - err = oxfw_volume_command(oxfw, &oxfw->volume_max, - 0, CTL_MAX, CTL_READ); - if (err < 0) - return err; - - err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); - if (err < 0) - return err; - - first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { - err = oxfw_volume_command(oxfw, &oxfw->volume[i], - first_ch + i, CTL_CURRENT, CTL_READ); - if (err < 0) - return err; - } - - for (i = 0; i < ARRAY_SIZE(controls); ++i) { - err = snd_ctl_add(oxfw->card, - snd_ctl_new1(&controls[i], oxfw)); - if (err < 0) - return err; - } - - return 0; -} - -static u32 oxfw_read_firmware_version(struct fw_unit *unit) -{ - __be32 data; - int err; - - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); - return err >= 0 ? be32_to_cpu(data) : 0; -} - -static void oxfw_card_free(struct snd_card *card) -{ - struct snd_oxfw *oxfw = card->private_data; - - amdtp_stream_destroy(&oxfw->rx_stream); - cmp_connection_destroy(&oxfw->in_conn); - fw_unit_put(oxfw->unit); - mutex_destroy(&oxfw->mutex); -} - -static int oxfw_probe(struct fw_unit *unit, - const struct ieee1394_device_id *id) -{ - struct fw_device *fw_dev = fw_parent_device(unit); - struct snd_card *card; - struct snd_oxfw *oxfw; - u32 firmware; - int err; - - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*oxfw), &card); - if (err < 0) - return err; - - oxfw = card->private_data; - oxfw->card = card; - mutex_init(&oxfw->mutex); - oxfw->unit = fw_unit_get(unit); - oxfw->device_info = (const struct device_info *)id->driver_data; - - err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0); - if (err < 0) - goto err_unit; - - err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_NONBLOCKING); - if (err < 0) - goto err_connection; - - card->private_free = oxfw_card_free; - - strcpy(card->driver, oxfw->device_info->driver_name); - strcpy(card->shortname, oxfw->device_info->short_name); - firmware = oxfw_read_firmware_version(unit); - snprintf(card->longname, sizeof(card->longname), - "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", - oxfw->device_info->long_name, - firmware >> 20, firmware & 0xffff, - fw_dev->config_rom[3], fw_dev->config_rom[4], - dev_name(&unit->device), 100 << fw_dev->max_speed); - strcpy(card->mixername, "OXFW"); - - err = oxfw_create_pcm(oxfw); - if (err < 0) - goto error; - - err = oxfw_create_mixer(oxfw); - if (err < 0) - goto error; - - err = snd_card_register(card); - if (err < 0) - goto error; - - dev_set_drvdata(&unit->device, oxfw); - - return 0; - -err_connection: - cmp_connection_destroy(&oxfw->in_conn); -err_unit: - fw_unit_put(oxfw->unit); - mutex_destroy(&oxfw->mutex); -error: - snd_card_free(card); - return err; -} - -static void oxfw_bus_reset(struct fw_unit *unit) -{ - struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); - - fcp_bus_reset(oxfw->unit); - - if (cmp_connection_update(&oxfw->in_conn) < 0) { - amdtp_stream_pcm_abort(&oxfw->rx_stream); - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); - return; - } - - amdtp_stream_update(&oxfw->rx_stream); -} - -static void oxfw_remove(struct fw_unit *unit) -{ - struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); - - amdtp_stream_pcm_abort(&oxfw->rx_stream); - snd_card_disconnect(oxfw->card); - - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); - - snd_card_free_when_closed(oxfw->card); -} - -static const struct device_info griffin_firewave = { - .driver_name = "FireWave", - .short_name = "FireWave", - .long_name = "Griffin FireWave Surround", - .pcm_constraints = firewave_constraints, - .mixer_channels = 6, - .mute_fb_id = 0x01, - .volume_fb_id = 0x02, -}; - -static const struct device_info lacie_speakers = { - .driver_name = "FWSpeakers", - .short_name = "FireWire Speakers", - .long_name = "LaCie FireWire Speakers", - .pcm_constraints = lacie_speakers_constraints, - .mixer_channels = 1, - .mute_fb_id = 0x01, - .volume_fb_id = 0x01, -}; - -static const struct ieee1394_device_id oxfw_id_table[] = { - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_GRIFFIN, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&griffin_firewave, - }, - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_LACIE, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&lacie_speakers, - }, - { } -}; -MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); - -static struct fw_driver oxfw_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - .bus = &fw_bus_type, - }, - .probe = oxfw_probe, - .update = oxfw_bus_reset, - .remove = oxfw_remove, - .id_table = oxfw_id_table, -}; - -static int __init snd_oxfw_init(void) -{ - return driver_register(&oxfw_driver.driver); -} - -static void __exit snd_oxfw_exit(void) -{ - driver_unregister(&oxfw_driver.driver); -} - -module_init(snd_oxfw_init); -module_exit(snd_oxfw_exit); diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile new file mode 100644 index 0000000..9ca49c0 --- /dev/null +++ b/sound/firewire/oxfw/Makefile @@ -0,0 +1,2 @@ +snd-oxfw-objs := oxfw.o +obj-m += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c new file mode 100644 index 0000000..55c687e --- /dev/null +++ b/sound/firewire/oxfw/oxfw.c @@ -0,0 +1,793 @@ +/* + * oxfw.c - a part of driver for OXFW970/971 based devices + * + * 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 "../cmp.h" +#include "../fcp.h" +#include "../amdtp.h" +#include "../lib.h" + +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ + +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 + +#define VENDOR_GRIFFIN 0x001292 +#define VENDOR_LACIE 0x00d04b + +#define SPECIFIER_1394TA 0x00a02d +#define VERSION_AVC 0x010001 + +struct device_info { + const char *driver_name; + const char *short_name; + const char *long_name; + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +struct snd_oxfw { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct mutex mutex; + struct cmp_connection in_conn; + struct amdtp_stream rx_stream; + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; +}; + +MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("snd-firewire-speakers"); + +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static unsigned int stereo_rates[] = { 48000, 96000 }; + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + /* two channels work only at 48/96 kHz */ + if (snd_interval_max(channels) < 6) + return snd_interval_list(rate, 2, stereo_rates, 0); + return 0; +} + +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + /* 32/44.1 kHz work only with all six channels */ + if (snd_interval_max(rate) < 48000) + return snd_interval_refine(channels, &all_channels); + return 0; +} + +static int firewave_constraints(struct snd_pcm_runtime *runtime) +{ + static unsigned int channels_list[] = { 2, 6 }; + static struct snd_pcm_hw_constraint_list channels_list_constraint = { + .count = 2, + .list = channels_list, + }; + int err; + + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000; + runtime->hw.channels_max = 6; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &channels_list_constraint); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + firewave_rate_constraint, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + firewave_channels_constraint, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + return 0; +} + +static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) +{ + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + + return 0; +} + +static int oxfw_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 = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->hw = hardware; + + err = oxfw->device_info->pcm_constraints(runtime); + if (err < 0) + return err; + err = snd_pcm_limit_hw_rates(runtime); + if (err < 0) + return err; + + err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); + if (err < 0) + return err; + + return 0; +} + +static int oxfw_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void oxfw_stop_stream(struct snd_oxfw *oxfw) +{ + if (amdtp_stream_running(&oxfw->rx_stream)) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + } +} + +static int oxfw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_stream_set_parameters(&oxfw->rx_stream, + params_rate(hw_params), + params_channels(hw_params), + 0); + + amdtp_stream_set_pcm_format(&oxfw->rx_stream, + params_format(hw_params)); + + err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params), + AVC_GENERAL_PLUG_DIR_IN, 0); + if (err < 0) { + dev_err(&oxfw->unit->device, "failed to set sample rate\n"); + goto err_buffer; + } + + return 0; + +err_buffer: + snd_pcm_lib_free_vmalloc_buffer(substream); +error: + return err; +} + +static int oxfw_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int oxfw_prepare(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + + if (amdtp_streaming_error(&oxfw->rx_stream)) + oxfw_stop_stream(oxfw); + + if (!amdtp_stream_running(&oxfw->rx_stream)) { + err = cmp_connection_establish(&oxfw->in_conn, + amdtp_stream_get_max_payload(&oxfw->rx_stream)); + if (err < 0) + goto err_mutex; + + err = amdtp_stream_start(&oxfw->rx_stream, + oxfw->in_conn.resources.channel, + oxfw->in_conn.speed); + if (err < 0) + goto err_connection; + } + + mutex_unlock(&oxfw->mutex); + + amdtp_stream_pcm_prepare(&oxfw->rx_stream); + + return 0; + +err_connection: + cmp_connection_break(&oxfw->in_conn); +err_mutex: + mutex_unlock(&oxfw->mutex); + + return err; +} + +static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); + return 0; +} + +static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + return amdtp_stream_pcm_pointer(&oxfw->rx_stream); +} + +static int oxfw_create_pcm(struct snd_oxfw *oxfw) +{ + static struct snd_pcm_ops ops = { + .open = oxfw_open, + .close = oxfw_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxfw_hw_params, + .hw_free = oxfw_hw_free, + .prepare = oxfw_prepare, + .trigger = oxfw_trigger, + .pointer = oxfw_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = oxfw; + strcpy(pcm->name, oxfw->device_info->short_name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); + return 0; +} + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + + value->value.integer.value[0] = !oxfw->mute; + + return 0; +} + +static int oxfw_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == oxfw->mute) + return 0; + + err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); + if (err < 0) + return err; + oxfw->mute = mute; + + return 1; +} + +static int oxfw_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct snd_oxfw *oxfw = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = oxfw->device_info->mixer_channels; + info->value.integer.min = oxfw->volume_min; + info->value.integer.max = oxfw->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int oxfw_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = oxfw->volume[i]; + + return 0; +} + +static int oxfw_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < oxfw->volume_min || + value->value.integer.value[i] > oxfw->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + oxfw->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = oxfw_volume_command(oxfw, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + oxfw->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +static int oxfw_create_mixer(struct snd_oxfw *oxfw) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = oxfw_mute_get, + .put = oxfw_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = oxfw_volume_info, + .get = oxfw_volume_get, + .put = oxfw_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = oxfw_volume_command(oxfw, &oxfw->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = oxfw_volume_command(oxfw, &oxfw->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + err = oxfw_volume_command(oxfw, &oxfw->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(oxfw->card, + snd_ctl_new1(&controls[i], oxfw)); + if (err < 0) + return err; + } + + return 0; +} + +static u32 oxfw_read_firmware_version(struct fw_unit *unit) +{ + __be32 data; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); + return err >= 0 ? be32_to_cpu(data) : 0; +} + +static void oxfw_card_free(struct snd_card *card) +{ + struct snd_oxfw *oxfw = card->private_data; + + amdtp_stream_destroy(&oxfw->rx_stream); + cmp_connection_destroy(&oxfw->in_conn); + fw_unit_put(oxfw->unit); + mutex_destroy(&oxfw->mutex); +} + +static int oxfw_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) +{ + struct fw_device *fw_dev = fw_parent_device(unit); + struct snd_card *card; + struct snd_oxfw *oxfw; + u32 firmware; + int err; + + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(*oxfw), &card); + if (err < 0) + return err; + + oxfw = card->private_data; + oxfw->card = card; + mutex_init(&oxfw->mutex); + oxfw->unit = fw_unit_get(unit); + oxfw->device_info = (const struct device_info *)id->driver_data; + + err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0); + if (err < 0) + goto err_unit; + + err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM, + CIP_NONBLOCKING); + if (err < 0) + goto err_connection; + + card->private_free = oxfw_card_free; + + strcpy(card->driver, oxfw->device_info->driver_name); + strcpy(card->shortname, oxfw->device_info->short_name); + firmware = oxfw_read_firmware_version(unit); + snprintf(card->longname, sizeof(card->longname), + "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + oxfw->device_info->long_name, + firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&unit->device), 100 << fw_dev->max_speed); + strcpy(card->mixername, "OXFW"); + + err = oxfw_create_pcm(oxfw); + if (err < 0) + goto error; + + err = oxfw_create_mixer(oxfw); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, oxfw); + + return 0; + +err_connection: + cmp_connection_destroy(&oxfw->in_conn); +err_unit: + fw_unit_put(oxfw->unit); + mutex_destroy(&oxfw->mutex); +error: + snd_card_free(card); + return err; +} + +static void oxfw_bus_reset(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + fcp_bus_reset(oxfw->unit); + + if (cmp_connection_update(&oxfw->in_conn) < 0) { + amdtp_stream_pcm_abort(&oxfw->rx_stream); + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + return; + } + + amdtp_stream_update(&oxfw->rx_stream); +} + +static void oxfw_remove(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + amdtp_stream_pcm_abort(&oxfw->rx_stream); + snd_card_disconnect(oxfw->card); + + mutex_lock(&oxfw->mutex); + oxfw_stop_stream(oxfw); + mutex_unlock(&oxfw->mutex); + + snd_card_free_when_closed(oxfw->card); +} + +static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .short_name = "FireWave", + .long_name = "Griffin FireWave Surround", + .pcm_constraints = firewave_constraints, + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, +}; + +static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .short_name = "FireWire Speakers", + .long_name = "LaCie FireWire Speakers", + .pcm_constraints = lacie_speakers_constraints, + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, +}; + +static const struct ieee1394_device_id oxfw_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_GRIFFIN, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&griffin_firewave, + }, + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LACIE, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&lacie_speakers, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); + +static struct fw_driver oxfw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = oxfw_probe, + .update = oxfw_bus_reset, + .remove = oxfw_remove, + .id_table = oxfw_id_table, +}; + +static int __init snd_oxfw_init(void) +{ + return driver_register(&oxfw_driver.driver); +} + +static void __exit snd_oxfw_exit(void) +{ + driver_unregister(&oxfw_driver.driver); +} + +module_init(snd_oxfw_init); +module_exit(snd_oxfw_exit); -- cgit v0.10.2 From e2786ca648d780d106bd8abca06746eb30d15ee7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:27 +0900 Subject: ALSA: oxfw: Split stream functionality to a new file and add a header file This is a help for works in followed patches. And this commit remove 'fw_unit_get()/fw_unit_put()' because these are called by helper functions in 'snd-firewire-lib'. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index 9ca49c0..e15c4c0 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -1,2 +1,2 @@ -snd-oxfw-objs := oxfw.o +snd-oxfw-objs := oxfw-stream.o oxfw.o obj-m += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c new file mode 100644 index 0000000..ebd156f --- /dev/null +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -0,0 +1,80 @@ +/* + * oxfw_stream.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw) +{ + int err; + + err = cmp_connection_init(&oxfw->in_conn, oxfw->unit, + CMP_INPUT, 0); + if (err < 0) + goto end; + + err = amdtp_stream_init(&oxfw->rx_stream, oxfw->unit, + AMDTP_OUT_STREAM, CIP_NONBLOCKING); + if (err < 0) { + amdtp_stream_destroy(&oxfw->rx_stream); + cmp_connection_destroy(&oxfw->in_conn); + } +end: + return err; +} + +static void stop_stream(struct snd_oxfw *oxfw) +{ + amdtp_stream_pcm_abort(&oxfw->rx_stream); + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); +} + +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw) +{ + int err = 0; + + if (amdtp_streaming_error(&oxfw->rx_stream)) + stop_stream(oxfw); + + if (amdtp_stream_running(&oxfw->rx_stream)) + goto end; + + err = cmp_connection_establish(&oxfw->in_conn, + amdtp_stream_get_max_payload(&oxfw->rx_stream)); + if (err < 0) + goto end; + + err = amdtp_stream_start(&oxfw->rx_stream, + oxfw->in_conn.resources.channel, + oxfw->in_conn.speed); + if (err < 0) + stop_stream(oxfw); +end: + return err; +} + +void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw) +{ + stop_stream(oxfw); +} + +void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw) +{ + stop_stream(oxfw); + + amdtp_stream_destroy(&oxfw->rx_stream); + cmp_connection_destroy(&oxfw->in_conn); +} + +void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw) +{ + if (cmp_connection_update(&oxfw->in_conn) < 0) + stop_stream(oxfw); + else + amdtp_stream_update(&oxfw->rx_stream); +} diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 55c687e..4055667 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -5,22 +5,7 @@ * 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 "../cmp.h" -#include "../fcp.h" -#include "../amdtp.h" -#include "../lib.h" +#include "oxfw.h" #define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) /* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ @@ -35,29 +20,6 @@ #define SPECIFIER_1394TA 0x00a02d #define VERSION_AVC 0x010001 -struct device_info { - const char *driver_name; - const char *short_name; - const char *long_name; - int (*pcm_constraints)(struct snd_pcm_runtime *runtime); - unsigned int mixer_channels; - u8 mute_fb_id; - u8 volume_fb_id; -}; - -struct snd_oxfw { - struct snd_card *card; - struct fw_unit *unit; - const struct device_info *device_info; - struct mutex mutex; - struct cmp_connection in_conn; - struct amdtp_stream rx_stream; - bool mute; - s16 volume[6]; - s16 volume_min; - s16 volume_max; -}; - MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); @@ -180,14 +142,6 @@ static int oxfw_close(struct snd_pcm_substream *substream) return 0; } -static void oxfw_stop_stream(struct snd_oxfw *oxfw) -{ - if (amdtp_stream_running(&oxfw->rx_stream)) { - amdtp_stream_stop(&oxfw->rx_stream); - cmp_connection_break(&oxfw->in_conn); - } -} - static int oxfw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { @@ -195,8 +149,8 @@ static int oxfw_hw_params(struct snd_pcm_substream *substream, int err; mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); + + snd_oxfw_stream_stop_simplex(oxfw); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); @@ -223,6 +177,7 @@ static int oxfw_hw_params(struct snd_pcm_substream *substream, err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: + mutex_unlock(&oxfw->mutex); return err; } @@ -231,7 +186,7 @@ static int oxfw_hw_free(struct snd_pcm_substream *substream) struct snd_oxfw *oxfw = substream->private_data; mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); + snd_oxfw_stream_stop_simplex(oxfw); mutex_unlock(&oxfw->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); @@ -244,33 +199,15 @@ static int oxfw_prepare(struct snd_pcm_substream *substream) mutex_lock(&oxfw->mutex); - if (amdtp_streaming_error(&oxfw->rx_stream)) - oxfw_stop_stream(oxfw); - - if (!amdtp_stream_running(&oxfw->rx_stream)) { - err = cmp_connection_establish(&oxfw->in_conn, - amdtp_stream_get_max_payload(&oxfw->rx_stream)); - if (err < 0) - goto err_mutex; + snd_oxfw_stream_stop_simplex(oxfw); - err = amdtp_stream_start(&oxfw->rx_stream, - oxfw->in_conn.resources.channel, - oxfw->in_conn.speed); - if (err < 0) - goto err_connection; - } - - mutex_unlock(&oxfw->mutex); + err = snd_oxfw_stream_start_simplex(oxfw); + if (err < 0) + goto end; amdtp_stream_pcm_prepare(&oxfw->rx_stream); - - return 0; - -err_connection: - cmp_connection_break(&oxfw->in_conn); -err_mutex: +end: mutex_unlock(&oxfw->mutex); - return err; } @@ -615,9 +552,6 @@ static void oxfw_card_free(struct snd_card *card) { struct snd_oxfw *oxfw = card->private_data; - amdtp_stream_destroy(&oxfw->rx_stream); - cmp_connection_destroy(&oxfw->in_conn); - fw_unit_put(oxfw->unit); mutex_destroy(&oxfw->mutex); } @@ -635,23 +569,13 @@ static int oxfw_probe(struct fw_unit *unit, if (err < 0) return err; + card->private_free = oxfw_card_free; oxfw = card->private_data; oxfw->card = card; mutex_init(&oxfw->mutex); - oxfw->unit = fw_unit_get(unit); + oxfw->unit = unit; oxfw->device_info = (const struct device_info *)id->driver_data; - err = cmp_connection_init(&oxfw->in_conn, unit, CMP_INPUT, 0); - if (err < 0) - goto err_unit; - - err = amdtp_stream_init(&oxfw->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_NONBLOCKING); - if (err < 0) - goto err_connection; - - card->private_free = oxfw_card_free; - strcpy(card->driver, oxfw->device_info->driver_name); strcpy(card->shortname, oxfw->device_info->short_name); firmware = oxfw_read_firmware_version(unit); @@ -671,19 +595,18 @@ static int oxfw_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_card_register(card); + err = snd_oxfw_stream_init_simplex(oxfw); if (err < 0) goto error; + err = snd_card_register(card); + if (err < 0) { + snd_oxfw_stream_destroy_simplex(oxfw); + goto error; + } dev_set_drvdata(&unit->device, oxfw); return 0; - -err_connection: - cmp_connection_destroy(&oxfw->in_conn); -err_unit: - fw_unit_put(oxfw->unit); - mutex_destroy(&oxfw->mutex); error: snd_card_free(card); return err; @@ -695,27 +618,18 @@ static void oxfw_bus_reset(struct fw_unit *unit) fcp_bus_reset(oxfw->unit); - if (cmp_connection_update(&oxfw->in_conn) < 0) { - amdtp_stream_pcm_abort(&oxfw->rx_stream); - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); - return; - } - - amdtp_stream_update(&oxfw->rx_stream); + mutex_lock(&oxfw->mutex); + snd_oxfw_stream_update_simplex(oxfw); + mutex_unlock(&oxfw->mutex); } static void oxfw_remove(struct fw_unit *unit) { struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); - amdtp_stream_pcm_abort(&oxfw->rx_stream); snd_card_disconnect(oxfw->card); - mutex_lock(&oxfw->mutex); - oxfw_stop_stream(oxfw); - mutex_unlock(&oxfw->mutex); + snd_oxfw_stream_destroy_simplex(oxfw); snd_card_free_when_closed(oxfw->card); } diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h new file mode 100644 index 0000000..e5836e0 --- /dev/null +++ b/sound/firewire/oxfw/oxfw.h @@ -0,0 +1,56 @@ +/* + * oxfw.h - a part of driver for OXFW970/971 based devices + * + * 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 "../lib.h" +#include "../fcp.h" +#include "../packets-buffer.h" +#include "../iso-resources.h" +#include "../amdtp.h" +#include "../cmp.h" + +struct device_info { + const char *driver_name; + const char *short_name; + const char *long_name; + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +struct snd_oxfw { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct mutex mutex; + struct cmp_connection in_conn; + struct amdtp_stream rx_stream; + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; +}; + +int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw); +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw); -- cgit v0.10.2 From 3713d93a6a12f8629c2660bb4a30d48b98105fca Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:28 +0900 Subject: ALSA: oxfw: Split PCM functionality to a new file This is a help for works in followed patches. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index e15c4c0..7fb4d09 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -1,2 +1,2 @@ -snd-oxfw-objs := oxfw-stream.o oxfw.o +snd-oxfw-objs := oxfw-stream.o oxfw-pcm.o oxfw.o obj-m += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c new file mode 100644 index 0000000..d39f17a --- /dev/null +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -0,0 +1,249 @@ +/* + * oxfw_pcm.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static unsigned int stereo_rates[] = { 48000, 96000 }; + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + /* two channels work only at 48/96 kHz */ + if (snd_interval_max(channels) < 6) + return snd_interval_list(rate, 2, stereo_rates, 0); + return 0; +} + +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + /* 32/44.1 kHz work only with all six channels */ + if (snd_interval_max(rate) < 48000) + return snd_interval_refine(channels, &all_channels); + return 0; +} + +int firewave_constraints(struct snd_pcm_runtime *runtime) +{ + static unsigned int channels_list[] = { 2, 6 }; + static struct snd_pcm_hw_constraint_list channels_list_constraint = { + .count = 2, + .list = channels_list, + }; + int err; + + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000; + runtime->hw.channels_max = 6; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &channels_list_constraint); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + firewave_rate_constraint, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + firewave_channels_constraint, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + return 0; +} + +int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) +{ + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + + return 0; +} + +static int pcm_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 = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + bool used; + int err; + + err = cmp_connection_check_used(&oxfw->in_conn, &used); + if ((err < 0) || used) + goto end; + + runtime->hw = hardware; + + err = oxfw->device_info->pcm_constraints(runtime); + if (err < 0) + goto end; + err = snd_pcm_limit_hw_rates(runtime); + if (err < 0) + goto end; + + err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); +end: + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + + snd_oxfw_stream_stop_simplex(oxfw); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_stream_set_parameters(&oxfw->rx_stream, + params_rate(hw_params), + params_channels(hw_params), + 0); + + amdtp_stream_set_pcm_format(&oxfw->rx_stream, + params_format(hw_params)); + + err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params), + AVC_GENERAL_PLUG_DIR_IN, 0); + if (err < 0) { + dev_err(&oxfw->unit->device, "failed to set sample rate\n"); + goto err_buffer; + } + + return 0; + +err_buffer: + snd_pcm_lib_free_vmalloc_buffer(substream); +error: + mutex_unlock(&oxfw->mutex); + return err; +} + +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + mutex_lock(&oxfw->mutex); + snd_oxfw_stream_stop_simplex(oxfw); + mutex_unlock(&oxfw->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + mutex_lock(&oxfw->mutex); + + snd_oxfw_stream_stop_simplex(oxfw); + + err = snd_oxfw_stream_start_simplex(oxfw); + if (err < 0) + goto end; + + amdtp_stream_pcm_prepare(&oxfw->rx_stream); +end: + mutex_unlock(&oxfw->mutex); + return err; +} + +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); + return 0; +} + +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + return amdtp_stream_pcm_pointer(&oxfw->rx_stream); +} + +int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) +{ + static struct snd_pcm_ops ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = oxfw; + strcpy(pcm->name, oxfw->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); + return 0; +} diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 4055667..24bb6df 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -25,244 +25,6 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("snd-firewire-speakers"); -static int firewave_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static unsigned int stereo_rates[] = { 48000, 96000 }; - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - - /* two channels work only at 48/96 kHz */ - if (snd_interval_max(channels) < 6) - return snd_interval_list(rate, 2, stereo_rates, 0); - return 0; -} - -static int firewave_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static const struct snd_interval all_channels = { .min = 6, .max = 6 }; - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - /* 32/44.1 kHz work only with all six channels */ - if (snd_interval_max(rate) < 48000) - return snd_interval_refine(channels, &all_channels); - return 0; -} - -static int firewave_constraints(struct snd_pcm_runtime *runtime) -{ - static unsigned int channels_list[] = { 2, 6 }; - static struct snd_pcm_hw_constraint_list channels_list_constraint = { - .count = 2, - .list = channels_list, - }; - int err; - - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000; - runtime->hw.channels_max = 6; - - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &channels_list_constraint); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - firewave_rate_constraint, NULL, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - firewave_channels_constraint, NULL, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - - return 0; -} - -static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) -{ - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000; - - return 0; -} - -static int oxfw_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 = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 4 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - runtime->hw = hardware; - - err = oxfw->device_info->pcm_constraints(runtime); - if (err < 0) - return err; - err = snd_pcm_limit_hw_rates(runtime); - if (err < 0) - return err; - - err = amdtp_stream_add_pcm_hw_constraints(&oxfw->rx_stream, runtime); - if (err < 0) - return err; - - return 0; -} - -static int oxfw_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -static int oxfw_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_oxfw *oxfw = substream->private_data; - int err; - - mutex_lock(&oxfw->mutex); - - snd_oxfw_stream_stop_simplex(oxfw); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - goto error; - - amdtp_stream_set_parameters(&oxfw->rx_stream, - params_rate(hw_params), - params_channels(hw_params), - 0); - - amdtp_stream_set_pcm_format(&oxfw->rx_stream, - params_format(hw_params)); - - err = avc_general_set_sig_fmt(oxfw->unit, params_rate(hw_params), - AVC_GENERAL_PLUG_DIR_IN, 0); - if (err < 0) { - dev_err(&oxfw->unit->device, "failed to set sample rate\n"); - goto err_buffer; - } - - return 0; - -err_buffer: - snd_pcm_lib_free_vmalloc_buffer(substream); -error: - mutex_unlock(&oxfw->mutex); - return err; -} - -static int oxfw_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - - mutex_lock(&oxfw->mutex); - snd_oxfw_stream_stop_simplex(oxfw); - mutex_unlock(&oxfw->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int oxfw_prepare(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - int err; - - mutex_lock(&oxfw->mutex); - - snd_oxfw_stream_stop_simplex(oxfw); - - err = snd_oxfw_stream_start_simplex(oxfw); - if (err < 0) - goto end; - - amdtp_stream_pcm_prepare(&oxfw->rx_stream); -end: - mutex_unlock(&oxfw->mutex); - return err; -} - -static int oxfw_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); - return 0; -} - -static snd_pcm_uframes_t oxfw_pointer(struct snd_pcm_substream *substream) -{ - struct snd_oxfw *oxfw = substream->private_data; - - return amdtp_stream_pcm_pointer(&oxfw->rx_stream); -} - -static int oxfw_create_pcm(struct snd_oxfw *oxfw) -{ - static struct snd_pcm_ops ops = { - .open = oxfw_open, - .close = oxfw_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = oxfw_hw_params, - .hw_free = oxfw_hw_free, - .prepare = oxfw_prepare, - .trigger = oxfw_trigger, - .pointer = oxfw_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(oxfw->card, "OXFW", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = oxfw; - strcpy(pcm->name, oxfw->device_info->short_name); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); - return 0; -} - enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, @@ -587,7 +349,7 @@ static int oxfw_probe(struct fw_unit *unit, dev_name(&unit->device), 100 << fw_dev->max_speed); strcpy(card->mixername, "OXFW"); - err = oxfw_create_pcm(oxfw); + err = snd_oxfw_create_pcm(oxfw); if (err < 0) goto error; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index e5836e0..1196db8 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -54,3 +54,7 @@ int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw); + +int firewave_constraints(struct snd_pcm_runtime *runtime); +int lacie_speakers_constraints(struct snd_pcm_runtime *runtime); +int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); -- cgit v0.10.2 From 31514bfb4ab8ba6f93b5ce5fcc543cb2ac4f96e5 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 29 Nov 2014 00:59:29 +0900 Subject: ALSA: oxfw: Split control functionality to a new file This is a help for works in followed patches. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index 7fb4d09..0cf48fd 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -1,2 +1,2 @@ -snd-oxfw-objs := oxfw-stream.o oxfw-pcm.o oxfw.o +snd-oxfw-objs := oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o obj-m += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-control.c new file mode 100644 index 0000000..02a1cb9 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-control.c @@ -0,0 +1,283 @@ +/* + * oxfw_stream.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + + value->value.integer.value[0] = !oxfw->mute; + + return 0; +} + +static int oxfw_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == oxfw->mute) + return 0; + + err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); + if (err < 0) + return err; + oxfw->mute = mute; + + return 1; +} + +static int oxfw_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct snd_oxfw *oxfw = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = oxfw->device_info->mixer_channels; + info->value.integer.min = oxfw->volume_min; + info->value.integer.max = oxfw->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int oxfw_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = oxfw->volume[i]; + + return 0; +} + +static int oxfw_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < oxfw->volume_min || + value->value.integer.value[i] > oxfw->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + oxfw->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = oxfw_volume_command(oxfw, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + oxfw->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +int snd_oxfw_create_mixer(struct snd_oxfw *oxfw) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = oxfw_mute_get, + .put = oxfw_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = oxfw_volume_info, + .get = oxfw_volume_get, + .put = oxfw_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = oxfw_volume_command(oxfw, &oxfw->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = oxfw_volume_command(oxfw, &oxfw->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + err = oxfw_volume_command(oxfw, &oxfw->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(oxfw->card, + snd_ctl_new1(&controls[i], oxfw)); + if (err < 0) + return err; + } + + return 0; +} diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 24bb6df..951d9a4 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -25,281 +25,6 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("snd-firewire-speakers"); -enum control_action { CTL_READ, CTL_WRITE }; -enum control_attribute { - CTL_MIN = 0x02, - CTL_MAX = 0x03, - CTL_CURRENT = 0x10, -}; - -static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(11, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ - buf[5] = 0x10; /* control attribute: current */ - buf[6] = 0x02; /* selector length */ - buf[7] = 0x00; /* audio channel number */ - buf[8] = 0x01; /* control selector: mute */ - buf[9] = 0x01; /* control data length */ - if (action == CTL_READ) - buf[10] = 0xff; - else - buf[10] = *value ? 0x70 : 0x60; - - err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); - if (err < 0) - goto error; - if (err < 11) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "mute command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = buf[10] == 0x70; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, - unsigned int channel, - enum control_attribute attribute, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(12, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ - buf[5] = attribute; /* control attribute */ - buf[6] = 0x02; /* selector length */ - buf[7] = channel; /* audio channel number */ - buf[8] = 0x02; /* control selector: volume */ - buf[9] = 0x02; /* control data length */ - if (action == CTL_READ) { - buf[10] = 0xff; - buf[11] = 0xff; - } else { - buf[10] = *value >> 8; - buf[11] = *value; - } - - err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); - if (err < 0) - goto error; - if (err < 12) { - dev_err(&oxfw->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&oxfw->unit->device, "volume command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = (buf[10] << 8) | buf[11]; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int oxfw_mute_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - - value->value.integer.value[0] = !oxfw->mute; - - return 0; -} - -static int oxfw_mute_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - bool mute; - int err; - - mute = !value->value.integer.value[0]; - - if (mute == oxfw->mute) - return 0; - - err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); - if (err < 0) - return err; - oxfw->mute = mute; - - return 1; -} - -static int oxfw_volume_info(struct snd_kcontrol *control, - struct snd_ctl_elem_info *info) -{ - struct snd_oxfw *oxfw = control->private_data; - - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = oxfw->device_info->mixer_channels; - info->value.integer.min = oxfw->volume_min; - info->value.integer.max = oxfw->volume_max; - - return 0; -} - -static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; - -static int oxfw_volume_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - unsigned int i; - - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - value->value.integer.value[channel_map[i]] = oxfw->volume[i]; - - return 0; -} - -static int oxfw_volume_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct snd_oxfw *oxfw = control->private_data; - unsigned int i, changed_channels; - bool equal_values = true; - s16 volume; - int err; - - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { - if (value->value.integer.value[i] < oxfw->volume_min || - value->value.integer.value[i] > oxfw->volume_max) - return -EINVAL; - if (value->value.integer.value[i] != - value->value.integer.value[0]) - equal_values = false; - } - - changed_channels = 0; - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) - if (value->value.integer.value[channel_map[i]] != - oxfw->volume[i]) - changed_channels |= 1 << (i + 1); - - if (equal_values && changed_channels != 0) - changed_channels = 1 << 0; - - for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { - volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; - if (changed_channels & (1 << i)) { - err = oxfw_volume_command(oxfw, &volume, i, - CTL_CURRENT, CTL_WRITE); - if (err < 0) - return err; - } - if (i > 0) - oxfw->volume[i - 1] = volume; - } - - return changed_channels != 0; -} - -static int oxfw_create_mixer(struct snd_oxfw *oxfw) -{ - static const struct snd_kcontrol_new controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = oxfw_mute_get, - .put = oxfw_mute_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .info = oxfw_volume_info, - .get = oxfw_volume_get, - .put = oxfw_volume_put, - }, - }; - unsigned int i, first_ch; - int err; - - err = oxfw_volume_command(oxfw, &oxfw->volume_min, - 0, CTL_MIN, CTL_READ); - if (err < 0) - return err; - err = oxfw_volume_command(oxfw, &oxfw->volume_max, - 0, CTL_MAX, CTL_READ); - if (err < 0) - return err; - - err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); - if (err < 0) - return err; - - first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; - for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { - err = oxfw_volume_command(oxfw, &oxfw->volume[i], - first_ch + i, CTL_CURRENT, CTL_READ); - if (err < 0) - return err; - } - - for (i = 0; i < ARRAY_SIZE(controls); ++i) { - err = snd_ctl_add(oxfw->card, - snd_ctl_new1(&controls[i], oxfw)); - if (err < 0) - return err; - } - - return 0; -} - static u32 oxfw_read_firmware_version(struct fw_unit *unit) { __be32 data; @@ -353,7 +78,7 @@ static int oxfw_probe(struct fw_unit *unit, if (err < 0) goto error; - err = oxfw_create_mixer(oxfw); + err = snd_oxfw_create_mixer(oxfw); if (err < 0) goto error; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 1196db8..6164bf3 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -58,3 +58,5 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw); int firewave_constraints(struct snd_pcm_runtime *runtime); int lacie_speakers_constraints(struct snd_pcm_runtime *runtime); int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); + +int snd_oxfw_create_mixer(struct snd_oxfw *oxfw); -- cgit v0.10.2 From 316638a5030a04bb3259dcbca0632281001a4b24 Mon Sep 17 00:00:00 2001 From: Kyle Chamberlin Date: Fri, 28 Nov 2014 13:59:56 -0500 Subject: ALSA: virmidi: fixed code style issues Fixed some minor code style issues and also removed some assignments inside of if conditionals. Signed-off-by: Kyle Chamberlin Signed-off-by: Takashi Iwai diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index b17872429..0af88d9 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -99,30 +99,33 @@ static int snd_virmidi_probe(struct platform_device *devptr) if (midi_devs[dev] > MAX_MIDI_DEVICES) { snd_printk(KERN_WARNING - "too much midi devices for virmidi %d: " - "force to use %d\n", dev, MAX_MIDI_DEVICES); + "too much midi devices for virmidi %d: force to use %d\n", + dev, MAX_MIDI_DEVICES); midi_devs[dev] = MAX_MIDI_DEVICES; } for (idx = 0; idx < midi_devs[dev]; idx++) { struct snd_rawmidi *rmidi; struct snd_virmidi_dev *rdev; - if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) + + err = snd_virmidi_new(card, idx, &rmidi); + if (err < 0) goto __nodev; rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; } - + strcpy(card->driver, "VirMIDI"); strcpy(card->shortname, "VirMIDI"); sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); - if ((err = snd_card_register(card)) == 0) { + err = snd_card_register(card); + if (err) { platform_set_drvdata(devptr, card); return 0; } - __nodev: +__nodev: snd_card_free(card); return err; } @@ -157,13 +160,15 @@ static int __init alsa_card_virmidi_init(void) { int i, cards, err; - if ((err = platform_driver_register(&snd_virmidi_driver)) < 0) + err = platform_driver_register(&snd_virmidi_driver); + if (err < 0) return err; cards = 0; for (i = 0; i < SNDRV_CARDS; i++) { struct platform_device *device; - if (! enable[i]) + + if (!enable[i]) continue; device = platform_device_register_simple(SND_VIRMIDI_DRIVER, i, NULL, 0); -- cgit v0.10.2 From 2f19cad94cee3c9bd52d0c9ca584ef506302fb7c Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 30 Nov 2014 08:56:33 -0500 Subject: btrfs: zero out left over bytes after processing compression streams Don Bailey noticed that our page zeroing for compression at end-io time isn't complete. This reworks a patch from Linus to push the zeroing into the zlib and lzo specific functions instead of trying to handle the corners inside btrfs_decompress_buf2page Signed-off-by: Chris Mason Reviewed-by: Josef Bacik Reported-by: Don A. Bailey cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index d3220d3..dcd9be3 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -1011,8 +1011,6 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start, bytes = min(bytes, working_bytes); kaddr = kmap_atomic(page_out); memcpy(kaddr + *pg_offset, buf + buf_offset, bytes); - if (*pg_index == (vcnt - 1) && *pg_offset == 0) - memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); kunmap_atomic(kaddr); flush_dcache_page(page_out); @@ -1054,3 +1052,34 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start, return 1; } + +/* + * When uncompressing data, we need to make sure and zero any parts of + * the biovec that were not filled in by the decompression code. pg_index + * and pg_offset indicate the last page and the last offset of that page + * that have been filled in. This will zero everything remaining in the + * biovec. + */ +void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt, + unsigned long pg_index, + unsigned long pg_offset) +{ + while (pg_index < vcnt) { + struct page *page = bvec[pg_index].bv_page; + unsigned long off = bvec[pg_index].bv_offset; + unsigned long len = bvec[pg_index].bv_len; + + if (pg_offset < off) + pg_offset = off; + if (pg_offset < off + len) { + unsigned long bytes = off + len - pg_offset; + char *kaddr; + + kaddr = kmap_atomic(page); + memset(kaddr + pg_offset, 0, bytes); + kunmap_atomic(kaddr); + } + pg_index++; + pg_offset = 0; + } +} diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index 0c803b4..d181f70 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h @@ -45,7 +45,9 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start, unsigned long nr_pages); int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio, int mirror_num, unsigned long bio_flags); - +void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt, + unsigned long pg_index, + unsigned long pg_offset); struct btrfs_compress_op { struct list_head *(*alloc_workspace)(void); diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 78285f3..617553c 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -373,6 +373,8 @@ cont: } done: kunmap(pages_in[page_in_index]); + if (!ret) + btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset); return ret; } @@ -410,10 +412,23 @@ static int lzo_decompress(struct list_head *ws, unsigned char *data_in, goto out; } + /* + * the caller is already checking against PAGE_SIZE, but lets + * move this check closer to the memcpy/memset + */ + destlen = min_t(unsigned long, destlen, PAGE_SIZE); bytes = min_t(unsigned long, destlen, out_len - start_byte); kaddr = kmap_atomic(dest_page); memcpy(kaddr, workspace->buf + start_byte, bytes); + + /* + * btrfs_getblock is doing a zero on the tail of the page too, + * but this will cover anything missing from the decompressed + * data. + */ + if (bytes < destlen) + memset(kaddr+bytes, 0, destlen-bytes); kunmap_atomic(kaddr); out: return ret; diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c index 759fa4e..fb22fd8 100644 --- a/fs/btrfs/zlib.c +++ b/fs/btrfs/zlib.c @@ -299,6 +299,8 @@ done: zlib_inflateEnd(&workspace->strm); if (data_in) kunmap(pages_in[page_in_index]); + if (!ret) + btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset); return ret; } @@ -310,10 +312,14 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in, struct workspace *workspace = list_entry(ws, struct workspace, list); int ret = 0; int wbits = MAX_WBITS; - unsigned long bytes_left = destlen; + unsigned long bytes_left; unsigned long total_out = 0; + unsigned long pg_offset = 0; char *kaddr; + destlen = min_t(unsigned long, destlen, PAGE_SIZE); + bytes_left = destlen; + workspace->strm.next_in = data_in; workspace->strm.avail_in = srclen; workspace->strm.total_in = 0; @@ -341,7 +347,6 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in, unsigned long buf_start; unsigned long buf_offset; unsigned long bytes; - unsigned long pg_offset = 0; ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) @@ -384,6 +389,17 @@ next: ret = 0; zlib_inflateEnd(&workspace->strm); + + /* + * this should only happen if zlib returned fewer bytes than we + * expected. btrfs_get_block is responsible for zeroing from the + * end of the inline extent (destlen) to the end of the page + */ + if (pg_offset < destlen) { + kaddr = kmap_atomic(dest_page); + memset(kaddr + pg_offset, 0, destlen - pg_offset); + kunmap_atomic(kaddr); + } return ret; } -- cgit v0.10.2 From fc5d3c70f97ba54bf3d3b585912b0a76a78fb718 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 30 Nov 2014 20:13:16 +0100 Subject: ALSA: virmidi: Fix wrong error check While rewriting the code in the previous commit [316638a5030a: ALSA: virmidi: fixed code style issues], the error check was wrongly converted. This resulted an Oops. Fixes: 316638a5030a ('ALSA: virmidi: fixed code style issues') Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 0af88d9..d28d870 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -121,7 +121,7 @@ static int snd_virmidi_probe(struct platform_device *devptr) sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); err = snd_card_register(card); - if (err) { + if (!err) { platform_set_drvdata(devptr, card); return 0; } -- cgit v0.10.2 From 009d0431c3914de64666bec0d350e54fdd59df6a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Nov 2014 16:42:27 -0800 Subject: Linux 3.18-rc7 diff --git a/Makefile b/Makefile index 2fd5c4e..ce70361 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 18 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Diseased Newt # *DOCUMENTATION* -- cgit v0.10.2 From dacacb0aa0cb6fdeb69313db6acfc82456945d7e Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Sun, 30 Nov 2014 18:45:40 +0200 Subject: ALSA: usb-audio: Add support for Zoom R16/24 capture and midi interfaces This makes the midi interface and capture work out of the box with R16 (and presumably R24 too but untested). Playback stream would also seem to function fine except for one caveat: no sound is produced, so it is disabled for now. Mixer descriptors are garbage and will require further quirks to enable functionality, also disabled here. Signed-off-by: Panu Matilainen Signed-off-by: Takashi Iwai diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 013cba8..73d2ba4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3175,6 +3175,46 @@ YAMAHA_DEVICE(0x7010, "UB99"), { /* + * ZOOM R16/24 in audio interface mode. + * Mixer descriptors are garbage, further quirks will be needed + * to make any of it functional, thus disabled for now. + * Playback stream appears to start and run fine but no sound + * is produced, so also disabled for now. + */ + USB_DEVICE(0x1686, 0x00dd), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + /* Mixer */ + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Playback */ + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Capture */ + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE, + }, + { + /* Midi */ + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + }, + } + } +}, + +{ + /* * Some USB MIDI devices don't have an audio control interface, * so we have to grab MIDI streaming interfaces here. */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e45cc3a..4dbfb3d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1207,6 +1207,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, break; } } + + /* Zoom R16/24 needs a tiny delay here, otherwise requests like + * get/set frequency return as failed despite actually succeeding. + */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && + (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(1); } /* -- cgit v0.10.2 From ea09dd3b006b26002802b7b534bdb6531429c8ff Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 2 Dec 2014 04:03:16 +0800 Subject: ALSA: dice: fix semicolon.cocci warnings sound/firewire/dice/dice-transaction.c:34:2-3: Unneeded semicolon Removes unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci CC: Takashi Sakamoto Signed-off-by: Fengguang Wu Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index a9b98e0..1fe304c 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -31,7 +31,7 @@ static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, default: offset += dice->global_offset; break; - }; + } offset += DICE_PRIVATE_SPACE; return offset; } -- cgit v0.10.2 From 87164cc5723329565089a999b6671bd214caf0a0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 18:05:32 +0100 Subject: ALSA: asihpi: Deletion of an unnecessary check before the function call "vfree" The vfree() function performs also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index e457eb8..6aa677e 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -547,8 +547,7 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) if (pa->irq) free_irq(pa->irq, pa); - if (pa->p_buffer) - vfree(pa->p_buffer); + vfree(pa->p_buffer); if (1) dev_info(&pci_dev->dev, -- cgit v0.10.2 From 057a4a55e703038d22bc9f2bcf8b02dc35850e16 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 18:34:45 +0100 Subject: ALSA: echoaudio: Deletion of a check before release_and_free_resource() The release_and_free_resource() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 60e4003..21228ad 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1875,8 +1875,7 @@ static int snd_echo_free(struct echoaudio *chip) if (chip->dsp_registers) iounmap(chip->dsp_registers); - if (chip->iores) - release_and_free_resource(chip->iores); + release_and_free_resource(chip->iores); pci_disable_device(chip->pci); -- cgit v0.10.2 From 5c34fdf48b9522ca87372b1fae19de8f93ffd130 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 18:52:21 +0100 Subject: ALSA: trident: Deletion of a check before snd_util_memhdr_free() The snd_util_memhdr_free() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index da875dc..57cd757 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3702,8 +3702,7 @@ static int snd_trident_free(struct snd_trident *trident) free_irq(trident->irq, trident); if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); - if (trident->tlb.memhdr) - snd_util_memhdr_free(trident->tlb.memhdr); + snd_util_memhdr_free(trident->tlb.memhdr); if (trident->tlb.silent_page.area) snd_dma_free_pages(&trident->tlb.silent_page); vfree(trident->tlb.shadow_entries); -- cgit v0.10.2 From 492a7ea0a6d3cc9c16ad5f8af832ed4278225b2e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Dec 2014 22:50:24 +0100 Subject: ALSA: i2sbus: Deletion of unnecessary checks before the function call "release_and_free_resource" The release_and_free_resource() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index a80d5ea..4e2b4fb 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -79,8 +79,7 @@ static void i2sbus_release_dev(struct device *dev) if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) - if (i2sdev->allocated_resource[i]) - release_and_free_resource(i2sdev->allocated_resource[i]); + release_and_free_resource(i2sdev->allocated_resource[i]); free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) @@ -323,8 +322,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, if (dev->out.dbdma) iounmap(dev->out.dbdma); if (dev->in.dbdma) iounmap(dev->in.dbdma); for (i=0;i<3;i++) - if (dev->allocated_resource[i]) - release_and_free_resource(dev->allocated_resource[i]); + release_and_free_resource(dev->allocated_resource[i]); mutex_destroy(&dev->lock); kfree(dev); return 0; -- cgit v0.10.2 From b42b4afb7482f1c079c82af824a7fe750590f438 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 3 Dec 2014 09:47:20 +0100 Subject: ALSA: hda - Define the DCAPS preset for the old Intel chipsets Just for improving readability. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fc7aff0..53e43d1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -276,6 +276,10 @@ enum { (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10) #define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10) +/* quirks for old Intel chipsets */ +#define AZX_DCAPS_INTEL_ICH \ + (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_BUFSIZE) + /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ (AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ @@ -2054,31 +2058,30 @@ static const struct pci_device_id azx_ids[] = { /* Braswell */ { PCI_DEVICE(0x8086, 0x2284), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, - /* ICH */ + /* ICH6 */ { PCI_DEVICE(0x8086, 0x2668), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH6 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH7 */ { PCI_DEVICE(0x8086, 0x27d8), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH7 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ESB2 */ { PCI_DEVICE(0x8086, 0x269a), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ESB2 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH8 */ { PCI_DEVICE(0x8086, 0x284b), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH8 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH9 */ { PCI_DEVICE(0x8086, 0x293e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH9 */ { PCI_DEVICE(0x8086, 0x293f), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH10 */ { PCI_DEVICE(0x8086, 0x3a3e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH10 */ { PCI_DEVICE(0x8086, 0x3a6e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, /* Generic Intel */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, -- cgit v0.10.2 From 103884a351a221553095c509a1dbbbf7d4fd9b05 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 3 Dec 2014 09:56:20 +0100 Subject: ALSA: hda - Drop AZX_DCAPS_ALIGN_BUFSIZE We introduced AZX_DCAPS_ALIGN_BUFSIZE to explicity show that the controller needs the alignment, with a slight hope that the buffer size alignment will be disabled as default in future. But the reality tells that most chips need the buffer size alignment, and it'll be likely enabled in future, too. This patch drops AZX_DCAPS_ALIGN_BUFSIZE to give back one more precious DCAPS bit for future use. At the same time, rename AZX_DCAPS_BUFSIZE with AZX_DCAPS_NO_ALIGN_BUFSIZE for avoiding confusion. AZX_DCAPS_ALIGN_BUFSIZE are still kept (but commented out) in each DCAPS presets for a purpose as markers. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 53e43d1..5ac0d39 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -278,24 +278,24 @@ enum { /* quirks for old Intel chipsets */ #define AZX_DCAPS_INTEL_ICH \ - (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_BUFSIZE) + (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE) /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ - (AZX_DCAPS_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ + (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ AZX_DCAPS_REVERSE_ASSIGN | AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) #define AZX_DCAPS_INTEL_HASWELL \ - (AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ + (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ - (AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_POSFIX_LPIB |\ + (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) @@ -315,7 +315,7 @@ enum { /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | AZX_DCAPS_ALIGN_BUFSIZE |\ + (AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \ AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\ AZX_DCAPS_SNOOP_TYPE(NVIDIA)) @@ -1568,10 +1568,8 @@ static int azx_first_init(struct azx *chip) if (align_buffer_size >= 0) chip->align_buffer_size = !!align_buffer_size; else { - if (chip->driver_caps & AZX_DCAPS_BUFSIZE) + if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE) chip->align_buffer_size = 0; - else if (chip->driver_caps & AZX_DCAPS_ALIGN_BUFSIZE) - chip->align_buffer_size = 1; else chip->align_buffer_size = 1; } @@ -2086,7 +2084,7 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_BUFSIZE }, + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE }, /* ATI SB 450/600/700/800/900 */ { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index a09703a..aa484fd 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -162,8 +162,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ -#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ +#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ +/* 22 unused */ #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ -- cgit v0.10.2 From 5395103dcc709d87f08edaecb786fc37781f3b22 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Dec 2014 09:59:31 -0800 Subject: ALSA: ctxfi: Neaten get_daio_rsc Move the pointer declarations into the blocks that use them. Neaten the kfree calls when the _init functions fail. Trivially reduces object size (defconfig x86-64) $ size sound/pci/ctxfi/ctdaio.o.* text data bss dec hex filename 5287 224 0 5511 1587 sound/pci/ctxfi/ctdaio.o.new 5319 224 0 5543 15a7 sound/pci/ctxfi/ctdaio.o.old Signed-off-by: Joe Perches Noticed-by: Markus Elfring Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index c1c3f88..9b87dd2 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -528,8 +528,6 @@ static int get_daio_rsc(struct daio_mgr *mgr, struct daio **rdaio) { int err; - struct dai *dai = NULL; - struct dao *dao = NULL; unsigned long flags; *rdaio = NULL; @@ -544,27 +542,30 @@ static int get_daio_rsc(struct daio_mgr *mgr, return err; } + err = -ENOMEM; /* Allocate mem for daio resource */ if (desc->type <= DAIO_OUT_MAX) { - dao = kzalloc(sizeof(*dao), GFP_KERNEL); - if (!dao) { - err = -ENOMEM; + struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL); + if (!dao) goto error; - } + err = dao_rsc_init(dao, desc, mgr); - if (err) + if (err) { + kfree(dao); goto error; + } *rdaio = &dao->daio; } else { - dai = kzalloc(sizeof(*dai), GFP_KERNEL); - if (!dai) { - err = -ENOMEM; + struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (!dai) goto error; - } + err = dai_rsc_init(dai, desc, mgr); - if (err) + if (err) { + kfree(dai); goto error; + } *rdaio = &dai->daio; } @@ -575,11 +576,6 @@ static int get_daio_rsc(struct daio_mgr *mgr, return 0; error: - if (dao) - kfree(dao); - else if (dai) - kfree(dai); - spin_lock_irqsave(&mgr->mgr_lock, flags); daio_mgr_put_rsc(&mgr->mgr, desc->type); spin_unlock_irqrestore(&mgr->mgr_lock, flags); -- cgit v0.10.2 From ca460cc250aa9d5255b3e2469ca0abad5d136233 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 5 Dec 2014 15:42:54 +0100 Subject: ALSA: sound/atmel/ac97c.c: Fix device index for pcm chip->pdev->id is -1 by default. This is an invalid index resulting in device file names like /dev/snd/pcmC0D-1p. Signed-off-by: Alexander Stein Signed-off-by: Takashi Iwai diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index b59427d..f1d1195 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -773,7 +773,7 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip) return err; } retval = snd_pcm_new(chip->card, chip->card->shortname, - chip->pdev->id, playback, capture, &pcm); + 0, playback, capture, &pcm); if (retval) return retval; -- cgit v0.10.2 From 1132015b16580d3f53385354ffec0f58443a1ffd Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 5 Dec 2014 20:10:06 +0100 Subject: ALSA: sound/atmel/ac97c.c: Add missing clock prepare Clocks must be prepared before enabling them. Do this in one step. Replace clk_enable with clk_prepare_enable and clk_disable with clk_disable_unprepare. This fixes the following warning: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:895 __clk_enable+0x24/0x9c() Modules linked in: CPU: 0 PID: 1 Comm: swapper Tainted: G W 3.18.0-rc7+ #245 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x60/0x80) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x18/0x20) [] (warn_slowpath_null) from [] (__clk_enable+0x24/0x9c) [] (__clk_enable) from [] (clk_enable+0x18/0x2c) [] (clk_enable) from [] (atmel_ac97c_probe+0x154/0x694) [] (atmel_ac97c_probe) from [] (platform_drv_probe+0x48/0x94) [] (platform_drv_probe) from [] (driver_probe_device+0x138/0x350) [] (driver_probe_device) from [] (__driver_attach+0x68/0x8c) [] (__driver_attach) from [] (bus_for_each_dev+0x70/0x84) [] (bus_for_each_dev) from [] (bus_add_driver+0xfc/0x1f8) [] (bus_add_driver) from [] (driver_register+0x9c/0xe0) [] (driver_register) from [] (do_one_initcall+0x110/0x1c8) [] (do_one_initcall) from [] (kernel_init_freeable+0xf8/0x1b8) [] (kernel_init_freeable) from [] (kernel_init+0x8/0xe4) [] (kernel_init) from [] (ret_from_fork+0x14/0x24) ---[ end trace cb88537fdc8fa201 ]--- atmel_ac97c fffa0000.sound: AC'97 0 does not respond - RESET atmel_ac97c fffa0000.sound: AC'97 0 access is not valid [0xffffffff], removing mixer. ------------[ cut here ]------------ Signed-off-by: Alexander Stein Signed-off-by: Takashi Iwai diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index f1d1195..cb44c74 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -944,7 +944,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "no peripheral clock\n"); return PTR_ERR(pclk); } - clk_enable(pclk); + clk_prepare_enable(pclk); retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, @@ -1122,7 +1122,7 @@ err_ioremap: err_request_irq: snd_card_free(card); err_snd_card_new: - clk_disable(pclk); + clk_disable_unprepare(pclk); clk_put(pclk); return retval; } @@ -1139,7 +1139,7 @@ static int atmel_ac97c_suspend(struct device *pdev) if (test_bit(DMA_TX_READY, &chip->flags)) dw_dma_cyclic_stop(chip->dma.tx_chan); } - clk_disable(chip->pclk); + clk_disable_unprepare(chip->pclk); return 0; } @@ -1149,7 +1149,7 @@ static int atmel_ac97c_resume(struct device *pdev) struct snd_card *card = dev_get_drvdata(pdev); struct atmel_ac97c *chip = card->private_data; - clk_enable(chip->pclk); + clk_prepare_enable(chip->pclk); if (cpu_is_at32ap7000()) { if (test_bit(DMA_RX_READY, &chip->flags)) dw_dma_cyclic_start(chip->dma.rx_chan); @@ -1177,7 +1177,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev) ac97c_writel(chip, COMR, 0); ac97c_writel(chip, MR, 0); - clk_disable(chip->pclk); + clk_disable_unprepare(chip->pclk); clk_put(chip->pclk); iounmap(chip->regs); free_irq(chip->irq, chip); -- cgit v0.10.2 From 048184540171672a724ab8f8bada7fcc0762f5c6 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 5 Dec 2014 20:10:07 +0100 Subject: ALSA: atmel_abdac: Add missing clock prepare Clocks must be prepared before enabling them. Do this in one step. Replace clk_enable with clk_prepare_enable and clk_disable with clk_disable_unprepare. Signed-off-by: Alexander Stein Signed-off-by: Takashi Iwai diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index 31061e3..0231405 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -242,7 +242,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ case SNDRV_PCM_TRIGGER_START: - clk_enable(dac->sample_clk); + clk_prepare_enable(dac->sample_clk); retval = dw_dma_cyclic_start(dac->dma.chan); if (retval) goto out; @@ -254,7 +254,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd) dw_dma_cyclic_stop(dac->dma.chan); dac_writel(dac, DATA, 0); dac_writel(dac, CTRL, 0); - clk_disable(dac->sample_clk); + clk_disable_unprepare(dac->sample_clk); break; default: retval = -EINVAL; @@ -429,7 +429,7 @@ static int atmel_abdac_probe(struct platform_device *pdev) retval = PTR_ERR(sample_clk); goto out_put_pclk; } - clk_enable(pclk); + clk_prepare_enable(pclk); retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, @@ -528,7 +528,7 @@ out_free_card: snd_card_free(card); out_put_sample_clk: clk_put(sample_clk); - clk_disable(pclk); + clk_disable_unprepare(pclk); out_put_pclk: clk_put(pclk); return retval; @@ -541,8 +541,8 @@ static int atmel_abdac_suspend(struct device *pdev) struct atmel_abdac *dac = card->private_data; dw_dma_cyclic_stop(dac->dma.chan); - clk_disable(dac->sample_clk); - clk_disable(dac->pclk); + clk_disable_unprepare(dac->sample_clk); + clk_disable_unprepare(dac->pclk); return 0; } @@ -552,8 +552,8 @@ static int atmel_abdac_resume(struct device *pdev) struct snd_card *card = dev_get_drvdata(pdev); struct atmel_abdac *dac = card->private_data; - clk_enable(dac->pclk); - clk_enable(dac->sample_clk); + clk_prepare_enable(dac->pclk); + clk_prepare_enable(dac->sample_clk); if (test_bit(DMA_READY, &dac->flags)) dw_dma_cyclic_start(dac->dma.chan); @@ -572,7 +572,7 @@ static int atmel_abdac_remove(struct platform_device *pdev) struct atmel_abdac *dac = get_dac(card); clk_put(dac->sample_clk); - clk_disable(dac->pclk); + clk_disable_unprepare(dac->pclk); clk_put(dac->pclk); dma_release_channel(dac->dma.chan); -- cgit v0.10.2