162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS. 462306a36Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of_platform.h> 1062306a36Sopenharmony_ci#include <linux/regmap.h> 1162306a36Sopenharmony_ci#include <linux/reset.h> 1262306a36Sopenharmony_ci#include <sound/soc.h> 1362306a36Sopenharmony_ci#include <sound/soc-dai.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <dt-bindings/sound/meson-aiu.h> 1662306a36Sopenharmony_ci#include "aiu.h" 1762306a36Sopenharmony_ci#include "aiu-fifo.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define AIU_I2S_MISC_958_SRC_SHIFT 3 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const char * const aiu_spdif_encode_sel_texts[] = { 2262306a36Sopenharmony_ci "SPDIF", "I2S", 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, 2662306a36Sopenharmony_ci AIU_I2S_MISC_958_SRC_SHIFT, 2762306a36Sopenharmony_ci aiu_spdif_encode_sel_texts); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct snd_kcontrol_new aiu_spdif_encode_mux = 3062306a36Sopenharmony_ci SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { 3362306a36Sopenharmony_ci SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, 3462306a36Sopenharmony_ci &aiu_spdif_encode_mux), 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { 3862306a36Sopenharmony_ci { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, 3962306a36Sopenharmony_ci { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, 4062306a36Sopenharmony_ci { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, 4162306a36Sopenharmony_ci { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciint aiu_of_xlate_dai_name(struct snd_soc_component *component, 4562306a36Sopenharmony_ci const struct of_phandle_args *args, 4662306a36Sopenharmony_ci const char **dai_name, 4762306a36Sopenharmony_ci unsigned int component_id) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct snd_soc_dai *dai; 5062306a36Sopenharmony_ci int id; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (args->args_count != 2) 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (args->args[0] != component_id) 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci id = args->args[1]; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (id < 0 || id >= component->num_dai) 6162306a36Sopenharmony_ci return -EINVAL; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci for_each_component_dais(component, dai) { 6462306a36Sopenharmony_ci if (id == 0) 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci id--; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci *dai_name = dai->driver->name; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component, 7562306a36Sopenharmony_ci const struct of_phandle_args *args, 7662306a36Sopenharmony_ci const char **dai_name) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int aiu_cpu_component_probe(struct snd_soc_component *component) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct aiu *aiu = snd_soc_component_get_drvdata(component); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Required for the SPDIF Source control operation */ 8662306a36Sopenharmony_ci return clk_prepare_enable(aiu->i2s.clks[PCLK].clk); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void aiu_cpu_component_remove(struct snd_soc_component *component) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct aiu *aiu = snd_soc_component_get_drvdata(component); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct snd_soc_component_driver aiu_cpu_component = { 9762306a36Sopenharmony_ci .name = "AIU CPU", 9862306a36Sopenharmony_ci .dapm_widgets = aiu_cpu_dapm_widgets, 9962306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(aiu_cpu_dapm_widgets), 10062306a36Sopenharmony_ci .dapm_routes = aiu_cpu_dapm_routes, 10162306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(aiu_cpu_dapm_routes), 10262306a36Sopenharmony_ci .of_xlate_dai_name = aiu_cpu_of_xlate_dai_name, 10362306a36Sopenharmony_ci .pointer = aiu_fifo_pointer, 10462306a36Sopenharmony_ci .probe = aiu_cpu_component_probe, 10562306a36Sopenharmony_ci .remove = aiu_cpu_component_remove, 10662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 10762306a36Sopenharmony_ci .debugfs_prefix = "cpu", 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { 11262306a36Sopenharmony_ci [CPU_I2S_FIFO] = { 11362306a36Sopenharmony_ci .name = "I2S FIFO", 11462306a36Sopenharmony_ci .playback = { 11562306a36Sopenharmony_ci .stream_name = "I2S FIFO Playback", 11662306a36Sopenharmony_ci .channels_min = 2, 11762306a36Sopenharmony_ci .channels_max = 8, 11862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 11962306a36Sopenharmony_ci .rate_min = 5512, 12062306a36Sopenharmony_ci .rate_max = 192000, 12162306a36Sopenharmony_ci .formats = AIU_FORMATS, 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci .ops = &aiu_fifo_i2s_dai_ops, 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci [CPU_SPDIF_FIFO] = { 12662306a36Sopenharmony_ci .name = "SPDIF FIFO", 12762306a36Sopenharmony_ci .playback = { 12862306a36Sopenharmony_ci .stream_name = "SPDIF FIFO Playback", 12962306a36Sopenharmony_ci .channels_min = 2, 13062306a36Sopenharmony_ci .channels_max = 2, 13162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 13262306a36Sopenharmony_ci .rate_min = 5512, 13362306a36Sopenharmony_ci .rate_max = 192000, 13462306a36Sopenharmony_ci .formats = AIU_FORMATS, 13562306a36Sopenharmony_ci }, 13662306a36Sopenharmony_ci .ops = &aiu_fifo_spdif_dai_ops, 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci [CPU_I2S_ENCODER] = { 13962306a36Sopenharmony_ci .name = "I2S Encoder", 14062306a36Sopenharmony_ci .playback = { 14162306a36Sopenharmony_ci .stream_name = "I2S Encoder Playback", 14262306a36Sopenharmony_ci .channels_min = 2, 14362306a36Sopenharmony_ci .channels_max = 8, 14462306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 14562306a36Sopenharmony_ci .formats = AIU_FORMATS, 14662306a36Sopenharmony_ci }, 14762306a36Sopenharmony_ci .ops = &aiu_encoder_i2s_dai_ops, 14862306a36Sopenharmony_ci }, 14962306a36Sopenharmony_ci [CPU_SPDIF_ENCODER] = { 15062306a36Sopenharmony_ci .name = "SPDIF Encoder", 15162306a36Sopenharmony_ci .playback = { 15262306a36Sopenharmony_ci .stream_name = "SPDIF Encoder Playback", 15362306a36Sopenharmony_ci .channels_min = 2, 15462306a36Sopenharmony_ci .channels_max = 2, 15562306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_32000 | 15662306a36Sopenharmony_ci SNDRV_PCM_RATE_44100 | 15762306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | 15862306a36Sopenharmony_ci SNDRV_PCM_RATE_88200 | 15962306a36Sopenharmony_ci SNDRV_PCM_RATE_96000 | 16062306a36Sopenharmony_ci SNDRV_PCM_RATE_176400 | 16162306a36Sopenharmony_ci SNDRV_PCM_RATE_192000), 16262306a36Sopenharmony_ci .formats = AIU_FORMATS, 16362306a36Sopenharmony_ci }, 16462306a36Sopenharmony_ci .ops = &aiu_encoder_spdif_dai_ops, 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const struct regmap_config aiu_regmap_cfg = { 16962306a36Sopenharmony_ci .reg_bits = 32, 17062306a36Sopenharmony_ci .val_bits = 32, 17162306a36Sopenharmony_ci .reg_stride = 4, 17262306a36Sopenharmony_ci .max_register = 0x2ac, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int aiu_clk_bulk_get(struct device *dev, 17662306a36Sopenharmony_ci const char * const *ids, 17762306a36Sopenharmony_ci unsigned int num, 17862306a36Sopenharmony_ci struct aiu_interface *interface) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct clk_bulk_data *clks; 18162306a36Sopenharmony_ci int i, ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci clks = devm_kcalloc(dev, num, sizeof(*clks), GFP_KERNEL); 18462306a36Sopenharmony_ci if (!clks) 18562306a36Sopenharmony_ci return -ENOMEM; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (i = 0; i < num; i++) 18862306a36Sopenharmony_ci clks[i].id = ids[i]; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, num, clks); 19162306a36Sopenharmony_ci if (ret < 0) 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci interface->clks = clks; 19562306a36Sopenharmony_ci interface->clk_num = num; 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const char * const aiu_i2s_ids[] = { 20062306a36Sopenharmony_ci [PCLK] = "i2s_pclk", 20162306a36Sopenharmony_ci [AOCLK] = "i2s_aoclk", 20262306a36Sopenharmony_ci [MCLK] = "i2s_mclk", 20362306a36Sopenharmony_ci [MIXER] = "i2s_mixer", 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const char * const aiu_spdif_ids[] = { 20762306a36Sopenharmony_ci [PCLK] = "spdif_pclk", 20862306a36Sopenharmony_ci [AOCLK] = "spdif_aoclk", 20962306a36Sopenharmony_ci [MCLK] = "spdif_mclk_sel" 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int aiu_clk_get(struct device *dev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct aiu *aiu = dev_get_drvdata(dev); 21562306a36Sopenharmony_ci struct clk *pclk; 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pclk = devm_clk_get_enabled(dev, "pclk"); 21962306a36Sopenharmony_ci if (IS_ERR(pclk)) 22062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n"); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); 22362306a36Sopenharmony_ci if (IS_ERR(aiu->spdif_mclk)) 22462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk), 22562306a36Sopenharmony_ci "Can't get the aiu spdif master clock\n"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids), 22862306a36Sopenharmony_ci &aiu->i2s); 22962306a36Sopenharmony_ci if (ret) 23062306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Can't get the i2s clocks\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids), 23362306a36Sopenharmony_ci &aiu->spdif); 23462306a36Sopenharmony_ci if (ret) 23562306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int aiu_probe(struct platform_device *pdev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 24362306a36Sopenharmony_ci void __iomem *regs; 24462306a36Sopenharmony_ci struct regmap *map; 24562306a36Sopenharmony_ci struct aiu *aiu; 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!aiu) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci aiu->platform = device_get_match_data(dev); 25362306a36Sopenharmony_ci if (!aiu->platform) 25462306a36Sopenharmony_ci return -ENODEV; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci platform_set_drvdata(pdev, aiu); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = device_reset(dev); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Failed to reset device\n"); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 26362306a36Sopenharmony_ci if (IS_ERR(regs)) 26462306a36Sopenharmony_ci return PTR_ERR(regs); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg); 26762306a36Sopenharmony_ci if (IS_ERR(map)) { 26862306a36Sopenharmony_ci dev_err(dev, "failed to init regmap: %ld\n", 26962306a36Sopenharmony_ci PTR_ERR(map)); 27062306a36Sopenharmony_ci return PTR_ERR(map); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s"); 27462306a36Sopenharmony_ci if (aiu->i2s.irq < 0) 27562306a36Sopenharmony_ci return aiu->i2s.irq; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif"); 27862306a36Sopenharmony_ci if (aiu->spdif.irq < 0) 27962306a36Sopenharmony_ci return aiu->spdif.irq; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ret = aiu_clk_get(dev); 28262306a36Sopenharmony_ci if (ret) 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Register the cpu component of the aiu */ 28662306a36Sopenharmony_ci ret = snd_soc_register_component(dev, &aiu_cpu_component, 28762306a36Sopenharmony_ci aiu_cpu_dai_drv, 28862306a36Sopenharmony_ci ARRAY_SIZE(aiu_cpu_dai_drv)); 28962306a36Sopenharmony_ci if (ret) { 29062306a36Sopenharmony_ci dev_err(dev, "Failed to register cpu component\n"); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Register the hdmi codec control component */ 29562306a36Sopenharmony_ci ret = aiu_hdmi_ctrl_register_component(dev); 29662306a36Sopenharmony_ci if (ret) { 29762306a36Sopenharmony_ci dev_err(dev, "Failed to register hdmi control component\n"); 29862306a36Sopenharmony_ci goto err; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Register the internal dac control component on gxl */ 30262306a36Sopenharmony_ci if (aiu->platform->has_acodec) { 30362306a36Sopenharmony_ci ret = aiu_acodec_ctrl_register_component(dev); 30462306a36Sopenharmony_ci if (ret) { 30562306a36Sopenharmony_ci dev_err(dev, 30662306a36Sopenharmony_ci "Failed to register acodec control component\n"); 30762306a36Sopenharmony_ci goto err; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_cierr: 31362306a36Sopenharmony_ci snd_soc_unregister_component(dev); 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void aiu_remove(struct platform_device *pdev) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic const struct aiu_platform_data aiu_gxbb_pdata = { 32362306a36Sopenharmony_ci .has_acodec = false, 32462306a36Sopenharmony_ci .has_clk_ctrl_more_i2s_div = true, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct aiu_platform_data aiu_gxl_pdata = { 32862306a36Sopenharmony_ci .has_acodec = true, 32962306a36Sopenharmony_ci .has_clk_ctrl_more_i2s_div = true, 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic const struct aiu_platform_data aiu_meson8_pdata = { 33362306a36Sopenharmony_ci .has_acodec = false, 33462306a36Sopenharmony_ci .has_clk_ctrl_more_i2s_div = false, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic const struct of_device_id aiu_of_match[] = { 33862306a36Sopenharmony_ci { .compatible = "amlogic,aiu-gxbb", .data = &aiu_gxbb_pdata }, 33962306a36Sopenharmony_ci { .compatible = "amlogic,aiu-gxl", .data = &aiu_gxl_pdata }, 34062306a36Sopenharmony_ci { .compatible = "amlogic,aiu-meson8", .data = &aiu_meson8_pdata }, 34162306a36Sopenharmony_ci { .compatible = "amlogic,aiu-meson8b", .data = &aiu_meson8_pdata }, 34262306a36Sopenharmony_ci {} 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aiu_of_match); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic struct platform_driver aiu_pdrv = { 34762306a36Sopenharmony_ci .probe = aiu_probe, 34862306a36Sopenharmony_ci .remove_new = aiu_remove, 34962306a36Sopenharmony_ci .driver = { 35062306a36Sopenharmony_ci .name = "meson-aiu", 35162306a36Sopenharmony_ci .of_match_table = aiu_of_match, 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_cimodule_platform_driver(aiu_pdrv); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciMODULE_DESCRIPTION("Meson AIU Driver"); 35762306a36Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 35862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 359