Re: [PATCH v3 2/2] ASOC: sunxi: Add support for the SPDIF block
From: Code Kipper
Date: Wed Feb 03 2016 - 03:41:48 EST
On 2 February 2016 at 22:17, Maxime Ripard
<maxime.ripard@xxxxxxxxxxxxxxxxxx> wrote:
> Hi,
>
> It looks mostly good on my side, a few comments though.
>
> On Tue, Feb 02, 2016 at 03:49:54PM +0100, codekipper@xxxxxxxxx wrote:
>> +#ifdef CONFIG_PM
>> +static int sun4i_spdif_runtime_suspend(struct device *dev)
>> +{
>> + struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
>> +
>> + clk_disable_unprepare(host->spdif_clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int sun4i_spdif_runtime_resume(struct device *dev)
>> +{
>> + struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
>> +
>> + clk_prepare_enable(host->spdif_clk);
>> +
>> + return 0;
>> +}
>> +#endif /* CONFIG_PM */
>
> You can also shut down the bus clock here. That will reset the device,
> so you'll have to reinit it in your resume, but since you won't be
> suspended while you play something, and that you set up most of your
> controller any way when you start streaming, it shouldn't be a big
> deal.
ACK
>
>> +
>> +static int sun4i_spdif_probe(struct platform_device *pdev)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + struct sun4i_spdif_dev *host;
>> + struct resource *res;
>> + int ret;
>> + void __iomem *base;
>> +
>> + dev_dbg(&pdev->dev, "Entered %s\n", __func__);
>> +
>> + if (!np)
>> + return -ENODEV;
>
> You won't probe without DT.
ACK
>
>> +
>> + if (!of_match_device(sun4i_spdif_of_match, &pdev->dev)) {
>> + dev_err(&pdev->dev, "No matched devices found.\n");
>> + return -EINVAL;
>> + }
>
> And you won't probe if the compatibles don't match.
ACK
>
>> + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
>> + if (!host)
>> + return -ENOMEM;
>> +
>> + host->pdev = pdev;
>> +
>> + /* Initialize this copy of the CPU DAI driver structure */
>> + memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
>> + host->cpu_dai_drv.name = dev_name(&pdev->dev);
>> +
>> + /* Get the addresses */
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(base))
>> + return PTR_ERR(base);
>> +
>> + host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
>> + &sun4i_spdif_regmap_config);
>> +
>> + /* Clocks */
>> + host->apb_clk = devm_clk_get(&pdev->dev, "apb");
>> + if (IS_ERR(host->apb_clk)) {
>> + dev_err(&pdev->dev, "failed to get a apb clock.\n");
>> + return PTR_ERR(host->apb_clk);
>> + }
>> +
>> + ret = clk_prepare_enable(host->apb_clk);
>> + if (ret) {
>> + dev_err(&pdev->dev, "try to enable apb_spdif_clk failed\n");
>> + return ret;
>> + }
>> +
>> + host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
>> + if (IS_ERR(host->spdif_clk)) {
>> + dev_err(&pdev->dev, "failed to get a spdif clock.\n");
>> + goto exit_clkdisable_apb_clk;
>> + }
>> +
>> + host->playback_supported = false;
>> + host->capture_supported = false;
>> +
>> + if (of_property_read_bool(np, "spdif-out"))
>> + host->playback_supported = true;
>> +
>> + if (!host->playback_supported) {
>> + dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
>> + goto exit_clkdisable_clk;
>> + }
>> +
>> + host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
>> + host->dma_params_tx.maxburst = 4;
>> + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>> + host->dma_params_rx.addr = res->start + SUN4I_SPDIF_RXFIFO;
>> + host->dma_params_rx.maxburst = 4;
>> + host->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>> +
>> + platform_set_drvdata(pdev, host);
>> +
>> + ret = devm_snd_soc_register_component(&pdev->dev,
>> + &sun4i_spdif_component, &sun4i_spdif_dai, 1);
>> + if (ret)
>> + goto exit_clkdisable_clk;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
>> + if (ret)
>> + goto exit_clkdisable_clk;
>> + return 0;
>> +
>> +exit_clkdisable_clk:
>> + clk_disable_unprepare(host->spdif_clk);
>
> You disabled a clock that you didn't enable.
ACK
>
>> +exit_clkdisable_apb_clk:
>> + clk_disable_unprepare(host->apb_clk);
>> + return ret;
>> +}
>> +
>> +static int sun4i_spdif_remove(struct platform_device *pdev)
>> +{
>> + struct sun4i_spdif_dev *host = dev_get_drvdata(&pdev->dev);
>> +
>> + snd_soc_unregister_platform(&pdev->dev);
>> + snd_soc_unregister_component(&pdev->dev);
>> +
>> + pm_runtime_disable(&pdev->dev);
>> +
>> + if (!IS_ERR(host->spdif_clk)) {
>
> Probe will fail on this condition. If probe failed, remove won't be
> called.
>
>> + clk_disable_unprepare(host->spdif_clk);
>
> And here as well.
>
>> + clk_disable_unprepare(host->apb_clk);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops sun4i_spdif_pm = {
>> + SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend,
>> + sun4i_spdif_runtime_resume, NULL)
>> +};
>> +
>> +static struct platform_driver sun4i_spdif_driver = {
>> + .driver = {
>> + .name = "sun4i-spdif",
>> + .owner = THIS_MODULE,
>
> This is not needed.
ACK
>
>> + .of_match_table = of_match_ptr(sun4i_spdif_of_match),
>> + .pm = &sun4i_spdif_pm,
>
> IIRC, the pm field is not defined if PM is not set.
>
> Have you tested your driver without PM?
No but I'll have a go!
>
>> + },
>> + .probe = sun4i_spdif_probe,
>> + .remove = sun4i_spdif_remove,
>> +};
>> +
>> +module_platform_driver(sun4i_spdif_driver);
>> +
>> +MODULE_AUTHOR("Marcus Cooper <codekipper@xxxxxxxxx>");
>> +MODULE_AUTHOR("Andrea Venturi <be17068@xxxxxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:sun4i-spdif");
>
> Thanks!
> Maxime
Thanks,
CK
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux, Kernel and Android engineering
> http://free-electrons.com