// SPDX-License-Identifier: GPL-2.0 /* * SHARP Brain (i.MX28) sound card: SAIF0/SAIF1 + ROHM BU26154. * * Loadable without device tree changes: the module creates the codec's I2C * device and its own platform device. SAIF0 outputs MCLK, BCLK and LRCLK. */ #include #include #include #include #include #include #include #include "mxs-saif.h" #define BRAIN_CODEC_BUS 0 #define BRAIN_CODEC_ADDR 0x1a /* HW_DIGCTL_CTRL: route the SAIF0 clock pins to SAIF1 as well (EXTMSTR0). */ #define DIGCTL_CTRL_BASE 0x8001c000 #define DIGCTL_SET 0x4 #define DIGCTL_CLR 0x8 #define DIGCTL_SAIF_CLKMUX(x) ((x) << 10) #define SAIF_CLKMUX_EXTMSTR0 0x2 static struct i2c_client *brain_codec_client; static struct platform_device *brain_sound_pdev; static int mxs_bu26154_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); unsigned int rate = params_rate(params); unsigned int mclk; int ret; /* The codec needs MCLK >= 4.096 MHz. */ mclk = rate >= 16000 ? 256 * rate : 512 * rate; ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 0); if (ret) return ret; return snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); } static const struct snd_soc_ops mxs_bu26154_ops = { .hw_params = mxs_bu26154_hw_params, }; #define MXS_BU26154_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ SND_SOC_DAIFMT_CBC_CFC) SND_SOC_DAILINK_DEFS(hifi_tx, DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_CODEC("bu26154.0-001a", "bu26154-hifi")), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(hifi_rx, DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_CODEC("bu26154.0-001a", "bu26154-hifi")), DAILINK_COMP_ARRAY(COMP_EMPTY())); static struct snd_soc_dai_link mxs_bu26154_dai[] = { { .name = "HiFi Tx", .stream_name = "HiFi Playback", .dai_fmt = MXS_BU26154_DAI_FMT, .ops = &mxs_bu26154_ops, .playback_only = true, SND_SOC_DAILINK_REG(hifi_tx), }, { .name = "HiFi Rx", .stream_name = "HiFi Capture", .dai_fmt = MXS_BU26154_DAI_FMT, .ops = &mxs_bu26154_ops, .capture_only = true, SND_SOC_DAILINK_REG(hifi_rx), }, }; static struct snd_soc_card mxs_bu26154_card = { .name = "brain-bu26154", .owner = THIS_MODULE, .dai_link = mxs_bu26154_dai, .num_links = ARRAY_SIZE(mxs_bu26154_dai), }; static int mxs_bu26154_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mxs_bu26154_card; static const char * const saif_alias[] = { "saif0", "saif1" }; struct device_node *np; void __iomem *digctl; int i, ret; for (i = 0; i < ARRAY_SIZE(saif_alias); i++) { np = of_find_node_by_path(saif_alias[i]); if (!np) { dev_err(&pdev->dev, "%s not found\n", saif_alias[i]); return -ENODEV; } mxs_bu26154_dai[i].cpus->of_node = np; mxs_bu26154_dai[i].platforms->of_node = np; of_node_put(np); } /* Brainux leaves the mux at DIRECT, which gives SAIF1 no clock. */ digctl = ioremap(DIGCTL_CTRL_BASE, 0x10); if (!digctl) return -ENOMEM; writel(DIGCTL_SAIF_CLKMUX(0x3), digctl + DIGCTL_CLR); writel(DIGCTL_SAIF_CLKMUX(SAIF_CLKMUX_EXTMSTR0), digctl + DIGCTL_SET); iounmap(digctl); ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); if (ret) { dev_err(&pdev->dev, "failed to get mclk\n"); return ret; } card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } static struct platform_driver mxs_bu26154_driver = { .driver = { .name = "mxs-bu26154", }, .probe = mxs_bu26154_probe, }; static int __init mxs_bu26154_init(void) { struct i2c_board_info info = { I2C_BOARD_INFO("bu26154", BRAIN_CODEC_ADDR), }; struct i2c_adapter *adapter; int ret; adapter = i2c_get_adapter(BRAIN_CODEC_BUS); if (!adapter) return -ENODEV; brain_codec_client = i2c_new_client_device(adapter, &info); i2c_put_adapter(adapter); if (IS_ERR(brain_codec_client)) return PTR_ERR(brain_codec_client); ret = platform_driver_register(&mxs_bu26154_driver); if (ret) goto err_client; brain_sound_pdev = platform_device_register_simple("mxs-bu26154", -1, NULL, 0); if (IS_ERR(brain_sound_pdev)) { ret = PTR_ERR(brain_sound_pdev); goto err_driver; } return 0; err_driver: platform_driver_unregister(&mxs_bu26154_driver); err_client: i2c_unregister_device(brain_codec_client); return ret; } module_init(mxs_bu26154_init); static void __exit mxs_bu26154_exit(void) { platform_device_unregister(brain_sound_pdev); platform_driver_unregister(&mxs_bu26154_driver); i2c_unregister_device(brain_codec_client); } module_exit(mxs_bu26154_exit); MODULE_DESCRIPTION("SHARP Brain BU26154 ALSA SoC machine driver"); MODULE_LICENSE("GPL");