0001
0002
0003
0004
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
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
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
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
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");