linux/sound/soc/xilinx/xilinx-dp-codec.c
<<
>>
Prefs
   1/*
   2 * Xilinx DisplayPort Sound Codec support
   3 *
   4 *  Copyright (C) 2015 Xilinx, Inc.
   5 *
   6 *  Author: Hyun Woo Kwon <hyunk@xilinx.com>
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include <linux/clk.h>
  19#include <linux/device.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22
  23#include <sound/soc.h>
  24
  25/**
  26 * struct xilinx_dp_codec - DisplayPort codec
  27 * @aud_clk: audio clock
  28 */
  29struct xilinx_dp_codec {
  30        struct clk *aud_clk;
  31};
  32
  33struct xilinx_dp_codec_fmt {
  34        unsigned long rate;
  35        unsigned int snd_rate;
  36};
  37
  38static struct snd_soc_dai_driver xilinx_dp_codec_dai = {
  39        .name           = "xilinx-dp-snd-codec-dai",
  40        .playback       = {
  41                .channels_min   = 2,
  42                .channels_max   = 2,
  43                .rates          = SNDRV_PCM_RATE_44100,
  44                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
  45        },
  46};
  47
  48static const struct xilinx_dp_codec_fmt rates[] = {
  49        {
  50                .rate   = 48000 * 512,
  51                .snd_rate = SNDRV_PCM_RATE_48000
  52        },
  53        {
  54                .rate   = 44100 * 512,
  55                .snd_rate = SNDRV_PCM_RATE_44100
  56        }
  57};
  58
  59static const struct snd_soc_codec_driver xilinx_dp_codec_codec_driver = {
  60};
  61
  62static int xilinx_dp_codec_probe(struct platform_device *pdev)
  63{
  64        struct xilinx_dp_codec *codec;
  65        unsigned int i;
  66        unsigned long rate;
  67        int ret;
  68
  69        codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL);
  70        if (!codec)
  71                return -ENOMEM;
  72
  73        codec->aud_clk = devm_clk_get(&pdev->dev, NULL);
  74        if (IS_ERR(codec->aud_clk))
  75                return PTR_ERR(codec->aud_clk);
  76
  77        ret = clk_prepare_enable(codec->aud_clk);
  78        if (ret) {
  79                dev_err(&pdev->dev, "failed to enable the aud_clk\n");
  80                return ret;
  81        }
  82
  83        for (i = 0; i < ARRAY_SIZE(rates); i++) {
  84                clk_disable_unprepare(codec->aud_clk);
  85                ret = clk_set_rate(codec->aud_clk, rates[i].rate);
  86                clk_prepare_enable(codec->aud_clk);
  87                if (ret)
  88                        continue;
  89
  90                rate = clk_get_rate(codec->aud_clk);
  91                /* Ignore some offset +- 10 */
  92                if (abs(rates[i].rate - rate) < 10) {
  93                        xilinx_dp_codec_dai.playback.rates = rates[i].snd_rate;
  94                        break;
  95                }
  96                ret = -EINVAL;
  97        }
  98
  99        if (ret) {
 100                dev_err(&pdev->dev, "Failed to get required clock freq\n");
 101                goto error_clk;
 102        }
 103
 104        ret = snd_soc_register_codec(&pdev->dev, &xilinx_dp_codec_codec_driver,
 105                                     &xilinx_dp_codec_dai, 1);
 106        if (ret)
 107                goto error_clk;
 108
 109        platform_set_drvdata(pdev, codec);
 110
 111        dev_info(&pdev->dev, "Xilinx DisplayPort Sound Codec probed\n");
 112
 113        return 0;
 114
 115error_clk:
 116        clk_disable_unprepare(codec->aud_clk);
 117        return ret;
 118}
 119
 120static int xilinx_dp_codec_dev_remove(struct platform_device *pdev)
 121{
 122        struct xilinx_dp_codec *codec = platform_get_drvdata(pdev);
 123
 124        snd_soc_unregister_codec(&pdev->dev);
 125        clk_disable_unprepare(codec->aud_clk);
 126
 127        return 0;
 128}
 129
 130static int __maybe_unused xilinx_dp_codec_pm_suspend(struct device *dev)
 131{
 132        struct xilinx_dp_codec *codec = dev_get_drvdata(dev);
 133
 134        clk_disable_unprepare(codec->aud_clk);
 135
 136        return 0;
 137}
 138
 139static int __maybe_unused xilinx_dp_codec_pm_resume(struct device *dev)
 140{
 141        struct xilinx_dp_codec *codec = dev_get_drvdata(dev);
 142        int ret;
 143
 144        ret = clk_prepare_enable(codec->aud_clk);
 145        if (ret)
 146                dev_err(dev, "failed to enable the aud_clk\n");
 147
 148        return ret;
 149}
 150
 151static const struct dev_pm_ops xilinx_dp_codec_pm_ops = {
 152        SET_SYSTEM_SLEEP_PM_OPS(xilinx_dp_codec_pm_suspend,
 153                                xilinx_dp_codec_pm_resume)
 154};
 155
 156static const struct of_device_id xilinx_dp_codec_of_match[] = {
 157        { .compatible = "xlnx,dp-snd-codec", },
 158        { /* end of table */ },
 159};
 160MODULE_DEVICE_TABLE(of, xilinx_dp_codec_of_match);
 161
 162static struct platform_driver xilinx_dp_codec_driver = {
 163        .driver = {
 164                .name           = "xilinx-dp-snd-codec",
 165                .of_match_table = xilinx_dp_codec_of_match,
 166                .pm             = &xilinx_dp_codec_pm_ops,
 167        },
 168        .probe  = xilinx_dp_codec_probe,
 169        .remove = xilinx_dp_codec_dev_remove,
 170};
 171module_platform_driver(xilinx_dp_codec_driver);
 172
 173MODULE_DESCRIPTION("Xilinx DisplayPort Sound Codec module");
 174MODULE_LICENSE("GPL v2");
 175