/* * Copyright (C) 2015 Linaro * * Author: Jun Nie * * License terms: GNU General Public License (GPL) version 2 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ZX_CTRL 0x04 #define ZX_FIFOCTRL 0x08 #define ZX_INT_STATUS 0x10 #define ZX_INT_MASK 0x14 #define ZX_DATA 0x18 #define ZX_VALID_BIT 0x1c #define ZX_CH_STA_1 0x20 #define ZX_CH_STA_2 0x24 #define ZX_CH_STA_3 0x28 #define ZX_CH_STA_4 0x2c #define ZX_CH_STA_5 0x30 #define ZX_CH_STA_6 0x34 #define ZX_CTRL_MODA_16 (0 << 6) #define ZX_CTRL_MODA_18 BIT(6) #define ZX_CTRL_MODA_20 (2 << 6) #define ZX_CTRL_MODA_24 (3 << 6) #define ZX_CTRL_MODA_MASK (3 << 6) #define ZX_CTRL_ENB BIT(4) #define ZX_CTRL_DNB (0 << 4) #define ZX_CTRL_ENB_MASK BIT(4) #define ZX_CTRL_TX_OPEN BIT(0) #define ZX_CTRL_TX_CLOSE (0 << 0) #define ZX_CTRL_TX_MASK BIT(0) #define ZX_CTRL_OPEN (ZX_CTRL_TX_OPEN | ZX_CTRL_ENB) #define ZX_CTRL_CLOSE (ZX_CTRL_TX_CLOSE | ZX_CTRL_DNB) #define ZX_CTRL_DOUBLE_TRACK (0 << 8) #define ZX_CTRL_LEFT_TRACK BIT(8) #define ZX_CTRL_RIGHT_TRACK (2 << 8) #define ZX_CTRL_TRACK_MASK (3 << 8) #define ZX_FIFOCTRL_TXTH_MASK (0x1f << 8) #define ZX_FIFOCTRL_TXTH(x) (x << 8) #define ZX_FIFOCTRL_TX_DMA_EN BIT(2) #define ZX_FIFOCTRL_TX_DMA_DIS (0 << 2) #define ZX_FIFOCTRL_TX_DMA_EN_MASK BIT(2) #define ZX_FIFOCTRL_TX_FIFO_RST BIT(0) #define ZX_FIFOCTRL_TX_FIFO_RST_MASK BIT(0) #define ZX_VALID_DOUBLE_TRACK (0 << 0) #define ZX_VALID_LEFT_TRACK BIT(1) #define ZX_VALID_RIGHT_TRACK (2 << 0) #define ZX_VALID_TRACK_MASK (3 << 0) #define ZX_SPDIF_CLK_RAT (2 * 32) struct zx_spdif_info { struct snd_dmaengine_dai_dma_data dma_data; struct clk *dai_clk; void __iomem *reg_base; resource_size_t mapbase; }; static int zx_spdif_dai_probe(struct snd_soc_dai *dai) { struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); snd_soc_dai_set_drvdata(dai, zx_spdif); zx_spdif->dma_data.addr = zx_spdif->mapbase + ZX_DATA; zx_spdif->dma_data.maxburst = 8; snd_soc_dai_init_dma_data(dai, &zx_spdif->dma_data, NULL); return 0; } static int zx_spdif_chanstats(void __iomem *base, unsigned int rate) { u32 cstas1; switch (rate) { case 22050: cstas1 = IEC958_AES3_CON_FS_22050; break; case 24000: cstas1 = IEC958_AES3_CON_FS_24000; break; case 32000: cstas1 = IEC958_AES3_CON_FS_32000; break; case 44100: cstas1 = IEC958_AES3_CON_FS_44100; break; case 48000: cstas1 = IEC958_AES3_CON_FS_48000; break; case 88200: cstas1 = IEC958_AES3_CON_FS_88200; break; case 96000: cstas1 = IEC958_AES3_CON_FS_96000; break; case 176400: cstas1 = IEC958_AES3_CON_FS_176400; break; case 192000: cstas1 = IEC958_AES3_CON_FS_192000; break; default: return -EINVAL; } cstas1 = cstas1 << 24; cstas1 |= IEC958_AES0_CON_NOT_COPYRIGHT; writel_relaxed(cstas1, base + ZX_CH_STA_1); return 0; } static int zx_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *socdai) { struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev); struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai); struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data; u32 val, ch_num, rate; int ret; dma_data = snd_soc_dai_get_dma_data(socdai, substream); dma_data->addr_width = params_width(params) >> 3; val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL); val &= ~ZX_CTRL_MODA_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val |= ZX_CTRL_MODA_16; break; case SNDRV_PCM_FORMAT_S18_3LE: val |= ZX_CTRL_MODA_18; break; case SNDRV_PCM_FORMAT_S20_3LE: val |= ZX_CTRL_MODA_20; break; case SNDRV_PCM_FORMAT_S24_LE: val |= ZX_CTRL_MODA_24; break; default: dev_err(socdai->dev, "Format not support!\n"); return -EINVAL; } ch_num = params_channels(params); if (ch_num == 2) val |= ZX_CTRL_DOUBLE_TRACK; else val |= ZX_CTRL_LEFT_TRACK; writel_relaxed(val, zx_spdif->reg_base + ZX_CTRL); val = readl_relaxed(zx_spdif->reg_base + ZX_VALID_BIT); val &= ~ZX_VALID_TRACK_MASK; if (ch_num == 2) val |= ZX_VALID_DOUBLE_TRACK; else val |= ZX_VALID_RIGHT_TRACK; writel_relaxed(val, zx_spdif->reg_base + ZX_VALID_BIT); rate = params_rate(params); ret = zx_spdif_chanstats(zx_spdif->reg_base, rate); if (ret) return ret; return clk_set_rate(spdif->dai_clk, rate * ch_num * ZX_SPDIF_CLK_RAT); } static void zx_spdif_cfg_tx(void __iomem *base, int on) { u32 val; val = readl_relaxed(base + ZX_CTRL); val &= ~(ZX_CTRL_ENB_MASK | ZX_CTRL_TX_MASK); val |= on ? ZX_CTRL_OPEN : ZX_CTRL_CLOSE; writel_relaxed(val, base + ZX_CTRL); val = readl_relaxed(base + ZX_FIFOCTRL); val &= ~ZX_FIFOCTRL_TX_DMA_EN_MASK; if (on) val |= ZX_FIFOCTRL_TX_DMA_EN; writel_relaxed(val, base + ZX_FIFOCTRL); } static int zx_spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { u32 val; struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: val = readl_relaxed(zx_spdif->reg_base + ZX_FIFOCTRL); val |= ZX_FIFOCTRL_TX_FIFO_RST; writel_relaxed(val, zx_spdif->reg_base + ZX_FIFOCTRL); /* fall thru */ case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: zx_spdif_cfg_tx(zx_spdif->reg_base, true); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: zx_spdif_cfg_tx(zx_spdif->reg_base, false); break; default: ret = -EINVAL; break; } return ret; } static int zx_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); return clk_prepare_enable(zx_spdif->dai_clk); } static void zx_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); clk_disable_unprepare(zx_spdif->dai_clk); } #define ZX_RATES \ (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) #define ZX_FORMAT \ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE \ | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_ops zx_spdif_dai_ops = { .trigger = zx_spdif_trigger, .startup = zx_spdif_startup, .shutdown = zx_spdif_shutdown, .hw_params = zx_spdif_hw_params, }; static struct snd_soc_dai_driver zx_spdif_dai = { .name = "spdif", .id = 0, .probe = zx_spdif_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, .rates = ZX_RATES, .formats = ZX_FORMAT, }, .ops = &zx_spdif_dai_ops, }; static const struct snd_soc_component_driver zx_spdif_component = { .name = "spdif", }; static void zx_spdif_dev_init(void __iomem *base) { u32 val; writel_relaxed(0, base + ZX_CTRL); writel_relaxed(0, base + ZX_INT_MASK); writel_relaxed(0xf, base + ZX_INT_STATUS); writel_relaxed(0x1, base + ZX_FIFOCTRL); val = readl_relaxed(base + ZX_FIFOCTRL); val &= ~(ZX_FIFOCTRL_TXTH_MASK | ZX_FIFOCTRL_TX_FIFO_RST_MASK); val |= ZX_FIFOCTRL_TXTH(8); writel_relaxed(val, base + ZX_FIFOCTRL); } static int zx_spdif_probe(struct platform_device *pdev) { struct resource *res; struct zx_spdif_info *zx_spdif; int ret; zx_spdif = devm_kzalloc(&pdev->dev, sizeof(*zx_spdif), GFP_KERNEL); if (!zx_spdif) return -ENOMEM; zx_spdif->dai_clk = devm_clk_get(&pdev->dev, "tx"); if (IS_ERR(zx_spdif->dai_clk)) { dev_err(&pdev->dev, "Fail to get clk\n"); return PTR_ERR(zx_spdif->dai_clk); } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); zx_spdif->mapbase = res->start; zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(zx_spdif->reg_base)) { dev_err(&pdev->dev, "ioremap failed!\n"); return PTR_ERR(zx_spdif->reg_base); } zx_spdif_dev_init(zx_spdif->reg_base); platform_set_drvdata(pdev, zx_spdif); ret = devm_snd_soc_register_component(&pdev->dev, &zx_spdif_component, &zx_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Register DAI failed: %d\n", ret); return ret; } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret); return ret; } static const struct of_device_id zx_spdif_dt_ids[] = { { .compatible = "zte,zx296702-spdif", }, {} }; MODULE_DEVICE_TABLE(of, zx_spdif_dt_ids); static struct platform_driver spdif_driver = { .probe = zx_spdif_probe, .driver = { .name = "zx-spdif", .of_match_table = zx_spdif_dt_ids, }, }; module_platform_driver(spdif_driver); MODULE_AUTHOR("Jun Nie "); MODULE_DESCRIPTION("ZTE SPDIF SoC DAI"); MODULE_LICENSE("GPL"); ded. An oops can be reproduced with the new fw_fallback.sh fallback mechanism cancellation test. Either cancelling the fallback mechanism or the custom fallback mechanism triggers a crash. mcgrof@piggy ~/linux-next/tools/testing/selftests/firmware (git::20170111-fw-fixes)$ sudo ./fw_fallback.sh ./fw_fallback.sh: timeout works ./fw_fallback.sh: firmware comparison works ./fw_fallback.sh: fallback mechanism works [ this then sits here when it is trying the cancellation test ] Kernel log: test_firmware: loading 'nope-test-firmware.bin' misc test_firmware: Direct firmware load for nope-test-firmware.bin failed with error -2 misc test_firmware: Falling back to user helper BUG: unable to handle kernel NULL pointer dereference at 0000000000000038 IP: _request_firmware+0xa27/0xad0 PGD 0 Oops: 0000 [#1] SMP Modules linked in: test_firmware(E) ... etc ... CPU: 1 PID: 1396 Comm: fw_fallback.sh Tainted: G W E 4.10.0-rc3-next-20170111+ #30 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.10.1-0-g8891697-prebuilt.qemu-project.org 04/01/2014 task: ffff9740b27f4340 task.stack: ffffbb15c0bc8000 RIP: 0010:_request_firmware+0xa27/0xad0 RSP: 0018:ffffbb15c0bcbd10 EFLAGS: 00010246 RAX: 00000000fffffffe RBX: ffff9740afe5aa80 RCX: 0000000000000000 RDX: ffff9740b27f4340 RSI: 0000000000000283 RDI: 0000000000000000 RBP: ffffbb15c0bcbd90 R08: ffffbb15c0bcbcd8 R09: 0000000000000000 R10: 0000000894a0d4b1 R11: 000000000000008c R12: ffffffffc0312480 R13: 0000000000000005 R14: ffff9740b1c32400 R15: 00000000000003e8 FS: 00007f8604422700(0000) GS:ffff9740bfc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000038 CR3: 000000012164c000 CR4: 00000000000006e0 Call Trace: request_firmware+0x37/0x50 trigger_request_store+0x79/0xd0 [test_firmware] dev_attr_store+0x18/0x30 sysfs_kf_write+0x37/0x40 kernfs_fop_write+0x110/0x1a0 __vfs_write+0x37/0x160 ? _cond_resched+0x1a/0x50 vfs_write+0xb5/0x1a0 SyS_write+0x55/0xc0 ? trace_do_page_fault+0x37/0xd0 entry_SYSCALL_64_fastpath+0x1e/0xad RIP: 0033:0x7f8603f49620 RSP: 002b:00007fff6287b788 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 000055c307b110a0 RCX: 00007f8603f49620 RDX: 0000000000000016 RSI: 000055c3084d8a90 RDI: 0000000000000001 RBP: 0000000000000016 R08: 000000000000c0ff R09: 000055c3084d6336 R10: 000055c307b108b0 R11: 0000000000000246 R12: 000055c307b13c80 R13: 000055c3084d6320 R14: 0000000000000000 R15: 00007fff6287b950 Code: 9f 64 84 e8 9c 61 fe ff b8 f4 ff ff ff e9 6b f9 ff ff 48 c7 c7 40 6b 8d 84 89 45 a8 e8 43 84 18 00 49 8b be 00 03 00 00 8b 45 a8 <83> 7f 38 02 74 08 e8 6e ec ff ff 8b 45 a8 49 c7 86 00 03 00 00 RIP: _request_firmware+0xa27/0xad0 RSP: ffffbb15c0bcbd10 CR2: 0000000000000038 ---[ end trace 6d94ac339c133e6f ]--- Fixes: 5d47ec02c37e ("firmware: Correct handling of fw_state_wait() return value") Reported-and-Tested-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reported-and-Tested-by: Patrick Bruenn <p.bruenn@beckhoff.com> Reported-by: Chris Wilson <chris@chris-wilson.co.uk> CC: <stable@vger.kernel.org> [3.10+] Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound/usb/bcd2000/bcd2000.c')