Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2020 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 #include <linux/bitfield.h>
0007 #include <linux/clk.h>
0008 #include <linux/module.h>
0009 #include <linux/of_platform.h>
0010 #include <linux/regmap.h>
0011 #include <linux/reset.h>
0012 #include <sound/soc.h>
0013 #include <sound/soc-dai.h>
0014 
0015 #include <dt-bindings/sound/meson-aiu.h>
0016 #include "aiu.h"
0017 #include "aiu-fifo.h"
0018 
0019 #define AIU_I2S_MISC_958_SRC_SHIFT 3
0020 
0021 static const char * const aiu_spdif_encode_sel_texts[] = {
0022     "SPDIF", "I2S",
0023 };
0024 
0025 static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC,
0026                 AIU_I2S_MISC_958_SRC_SHIFT,
0027                 aiu_spdif_encode_sel_texts);
0028 
0029 static const struct snd_kcontrol_new aiu_spdif_encode_mux =
0030     SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
0031 
0032 static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = {
0033     SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0,
0034              &aiu_spdif_encode_mux),
0035 };
0036 
0037 static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = {
0038     { "I2S Encoder Playback", NULL, "I2S FIFO Playback" },
0039     { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" },
0040     { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" },
0041     { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" },
0042 };
0043 
0044 int aiu_of_xlate_dai_name(struct snd_soc_component *component,
0045               const struct of_phandle_args *args,
0046               const char **dai_name,
0047               unsigned int component_id)
0048 {
0049     struct snd_soc_dai *dai;
0050     int id;
0051 
0052     if (args->args_count != 2)
0053         return -EINVAL;
0054 
0055     if (args->args[0] != component_id)
0056         return -EINVAL;
0057 
0058     id = args->args[1];
0059 
0060     if (id < 0 || id >= component->num_dai)
0061         return -EINVAL;
0062 
0063     for_each_component_dais(component, dai) {
0064         if (id == 0)
0065             break;
0066         id--;
0067     }
0068 
0069     *dai_name = dai->driver->name;
0070 
0071     return 0;
0072 }
0073 
0074 static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
0075                      const struct of_phandle_args *args,
0076                      const char **dai_name)
0077 {
0078     return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU);
0079 }
0080 
0081 static int aiu_cpu_component_probe(struct snd_soc_component *component)
0082 {
0083     struct aiu *aiu = snd_soc_component_get_drvdata(component);
0084 
0085     /* Required for the SPDIF Source control operation */
0086     return clk_prepare_enable(aiu->i2s.clks[PCLK].clk);
0087 }
0088 
0089 static void aiu_cpu_component_remove(struct snd_soc_component *component)
0090 {
0091     struct aiu *aiu = snd_soc_component_get_drvdata(component);
0092 
0093     clk_disable_unprepare(aiu->i2s.clks[PCLK].clk);
0094 }
0095 
0096 static const struct snd_soc_component_driver aiu_cpu_component = {
0097     .name           = "AIU CPU",
0098     .dapm_widgets       = aiu_cpu_dapm_widgets,
0099     .num_dapm_widgets   = ARRAY_SIZE(aiu_cpu_dapm_widgets),
0100     .dapm_routes        = aiu_cpu_dapm_routes,
0101     .num_dapm_routes    = ARRAY_SIZE(aiu_cpu_dapm_routes),
0102     .of_xlate_dai_name  = aiu_cpu_of_xlate_dai_name,
0103     .pointer        = aiu_fifo_pointer,
0104     .probe          = aiu_cpu_component_probe,
0105     .remove         = aiu_cpu_component_remove,
0106 #ifdef CONFIG_DEBUG_FS
0107     .debugfs_prefix     = "cpu",
0108 #endif
0109 };
0110 
0111 static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
0112     [CPU_I2S_FIFO] = {
0113         .name = "I2S FIFO",
0114         .playback = {
0115             .stream_name    = "I2S FIFO Playback",
0116             .channels_min   = 2,
0117             .channels_max   = 8,
0118             .rates      = SNDRV_PCM_RATE_CONTINUOUS,
0119             .rate_min   = 5512,
0120             .rate_max   = 192000,
0121             .formats    = AIU_FORMATS,
0122         },
0123         .ops        = &aiu_fifo_i2s_dai_ops,
0124         .pcm_new    = aiu_fifo_pcm_new,
0125         .probe      = aiu_fifo_i2s_dai_probe,
0126         .remove     = aiu_fifo_dai_remove,
0127     },
0128     [CPU_SPDIF_FIFO] = {
0129         .name = "SPDIF FIFO",
0130         .playback = {
0131             .stream_name    = "SPDIF FIFO Playback",
0132             .channels_min   = 2,
0133             .channels_max   = 2,
0134             .rates      = SNDRV_PCM_RATE_CONTINUOUS,
0135             .rate_min   = 5512,
0136             .rate_max   = 192000,
0137             .formats    = AIU_FORMATS,
0138         },
0139         .ops        = &aiu_fifo_spdif_dai_ops,
0140         .pcm_new    = aiu_fifo_pcm_new,
0141         .probe      = aiu_fifo_spdif_dai_probe,
0142         .remove     = aiu_fifo_dai_remove,
0143     },
0144     [CPU_I2S_ENCODER] = {
0145         .name = "I2S Encoder",
0146         .playback = {
0147             .stream_name = "I2S Encoder Playback",
0148             .channels_min = 2,
0149             .channels_max = 8,
0150             .rates = SNDRV_PCM_RATE_8000_192000,
0151             .formats = AIU_FORMATS,
0152         },
0153         .ops = &aiu_encoder_i2s_dai_ops,
0154     },
0155     [CPU_SPDIF_ENCODER] = {
0156         .name = "SPDIF Encoder",
0157         .playback = {
0158             .stream_name = "SPDIF Encoder Playback",
0159             .channels_min = 2,
0160             .channels_max = 2,
0161             .rates = (SNDRV_PCM_RATE_32000  |
0162                   SNDRV_PCM_RATE_44100  |
0163                   SNDRV_PCM_RATE_48000  |
0164                   SNDRV_PCM_RATE_88200  |
0165                   SNDRV_PCM_RATE_96000  |
0166                   SNDRV_PCM_RATE_176400 |
0167                   SNDRV_PCM_RATE_192000),
0168             .formats = AIU_FORMATS,
0169         },
0170         .ops = &aiu_encoder_spdif_dai_ops,
0171     }
0172 };
0173 
0174 static const struct regmap_config aiu_regmap_cfg = {
0175     .reg_bits   = 32,
0176     .val_bits   = 32,
0177     .reg_stride = 4,
0178     .max_register   = 0x2ac,
0179 };
0180 
0181 static int aiu_clk_bulk_get(struct device *dev,
0182                 const char * const *ids,
0183                 unsigned int num,
0184                 struct aiu_interface *interface)
0185 {
0186     struct clk_bulk_data *clks;
0187     int i, ret;
0188 
0189     clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL);
0190     if (!clks)
0191         return -ENOMEM;
0192 
0193     for (i = 0; i < num; i++)
0194         clks[i].id = ids[i];
0195 
0196     ret = devm_clk_bulk_get(dev, num, clks);
0197     if (ret < 0)
0198         return ret;
0199 
0200     interface->clks = clks;
0201     interface->clk_num = num;
0202     return 0;
0203 }
0204 
0205 static const char * const aiu_i2s_ids[] = {
0206     [PCLK]  = "i2s_pclk",
0207     [AOCLK] = "i2s_aoclk",
0208     [MCLK]  = "i2s_mclk",
0209     [MIXER] = "i2s_mixer",
0210 };
0211 
0212 static const char * const aiu_spdif_ids[] = {
0213     [PCLK]  = "spdif_pclk",
0214     [AOCLK] = "spdif_aoclk",
0215     [MCLK]  = "spdif_mclk_sel"
0216 };
0217 
0218 static int aiu_clk_get(struct device *dev)
0219 {
0220     struct aiu *aiu = dev_get_drvdata(dev);
0221     int ret;
0222 
0223     aiu->pclk = devm_clk_get(dev, "pclk");
0224     if (IS_ERR(aiu->pclk))
0225         return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n");
0226 
0227     aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
0228     if (IS_ERR(aiu->spdif_mclk))
0229         return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk),
0230                      "Can't get the aiu spdif master clock\n");
0231 
0232     ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
0233                    &aiu->i2s);
0234     if (ret)
0235         return dev_err_probe(dev, ret, "Can't get the i2s clocks\n");
0236 
0237     ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
0238                    &aiu->spdif);
0239     if (ret)
0240         return dev_err_probe(dev, ret, "Can't get the spdif clocks\n");
0241 
0242     ret = clk_prepare_enable(aiu->pclk);
0243     if (ret) {
0244         dev_err(dev, "peripheral clock enable failed\n");
0245         return ret;
0246     }
0247 
0248     ret = devm_add_action_or_reset(dev,
0249                        (void(*)(void *))clk_disable_unprepare,
0250                        aiu->pclk);
0251     if (ret)
0252         dev_err(dev, "failed to add reset action on pclk");
0253 
0254     return ret;
0255 }
0256 
0257 static int aiu_probe(struct platform_device *pdev)
0258 {
0259     struct device *dev = &pdev->dev;
0260     void __iomem *regs;
0261     struct regmap *map;
0262     struct aiu *aiu;
0263     int ret;
0264 
0265     aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
0266     if (!aiu)
0267         return -ENOMEM;
0268 
0269     aiu->platform = device_get_match_data(dev);
0270     if (!aiu->platform)
0271         return -ENODEV;
0272 
0273     platform_set_drvdata(pdev, aiu);
0274 
0275     ret = device_reset(dev);
0276     if (ret)
0277         return dev_err_probe(dev, ret, "Failed to reset device\n");
0278 
0279     regs = devm_platform_ioremap_resource(pdev, 0);
0280     if (IS_ERR(regs))
0281         return PTR_ERR(regs);
0282 
0283     map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
0284     if (IS_ERR(map)) {
0285         dev_err(dev, "failed to init regmap: %ld\n",
0286             PTR_ERR(map));
0287         return PTR_ERR(map);
0288     }
0289 
0290     aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
0291     if (aiu->i2s.irq < 0)
0292         return aiu->i2s.irq;
0293 
0294     aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
0295     if (aiu->spdif.irq < 0)
0296         return aiu->spdif.irq;
0297 
0298     ret = aiu_clk_get(dev);
0299     if (ret)
0300         return ret;
0301 
0302     /* Register the cpu component of the aiu */
0303     ret = snd_soc_register_component(dev, &aiu_cpu_component,
0304                      aiu_cpu_dai_drv,
0305                      ARRAY_SIZE(aiu_cpu_dai_drv));
0306     if (ret) {
0307         dev_err(dev, "Failed to register cpu component\n");
0308         return ret;
0309     }
0310 
0311     /* Register the hdmi codec control component */
0312     ret = aiu_hdmi_ctrl_register_component(dev);
0313     if (ret) {
0314         dev_err(dev, "Failed to register hdmi control component\n");
0315         goto err;
0316     }
0317 
0318     /* Register the internal dac control component on gxl */
0319     if (aiu->platform->has_acodec) {
0320         ret = aiu_acodec_ctrl_register_component(dev);
0321         if (ret) {
0322             dev_err(dev,
0323                 "Failed to register acodec control component\n");
0324             goto err;
0325         }
0326     }
0327 
0328     return 0;
0329 err:
0330     snd_soc_unregister_component(dev);
0331     return ret;
0332 }
0333 
0334 static int aiu_remove(struct platform_device *pdev)
0335 {
0336     snd_soc_unregister_component(&pdev->dev);
0337 
0338     return 0;
0339 }
0340 
0341 static const struct aiu_platform_data aiu_gxbb_pdata = {
0342     .has_acodec = false,
0343     .has_clk_ctrl_more_i2s_div = true,
0344 };
0345 
0346 static const struct aiu_platform_data aiu_gxl_pdata = {
0347     .has_acodec = true,
0348     .has_clk_ctrl_more_i2s_div = true,
0349 };
0350 
0351 static const struct aiu_platform_data aiu_meson8_pdata = {
0352     .has_acodec = false,
0353     .has_clk_ctrl_more_i2s_div = false,
0354 };
0355 
0356 static const struct of_device_id aiu_of_match[] = {
0357     { .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata },
0358     { .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata },
0359     { .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata },
0360     { .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata },
0361     {}
0362 };
0363 MODULE_DEVICE_TABLE(of, aiu_of_match);
0364 
0365 static struct platform_driver aiu_pdrv = {
0366     .probe = aiu_probe,
0367     .remove = aiu_remove,
0368     .driver = {
0369         .name = "meson-aiu",
0370         .of_match_table = aiu_of_match,
0371     },
0372 };
0373 module_platform_driver(aiu_pdrv);
0374 
0375 MODULE_DESCRIPTION("Meson AIU Driver");
0376 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0377 MODULE_LICENSE("GPL v2");