/* * sound\soc\sunxi\sunxi-mad.c * (C) Copyright 2018-2023 * AllWinner Technology Co., Ltd. * wolfgang * * some simple description for this code * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sunxi-mad.h" #define DRV_NAME "sunxi-mad" struct sunxi_mad_info { struct device *dev; struct clk *mad_clk; struct clk *mad_cfg_clk; struct clk *mad_ad_clk; struct clk *lpsd_clk; struct clk *pll_clk; struct clk *hosc_clk; unsigned int pll_audio_src_used; unsigned int hosc_src_used; }; struct regmap *sunxi_mad_regmap; /*memory mapping*/ #define MAD_SRAM_DMA_SRC_ADDR 0x05480000 /*256kbytes*/ #define MAD_SRAM_SIZE_VALUE 0x100 /*from cpu's ASR algorithm*/ #define MAD_SRAM_STORE_TH_VALUE 0X0 void sunxi_lpsd_init(void) { /*config sunxi_mad_sram_wake_back_data_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_WAKE_BACK_DATA, 0x5); /*config sunxi_mad_lpsd_ad_sync_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_LPSD_AD_SYNC_FC, 0x20); /*config sunxi_mad_lpsd_th_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_LPSD_TH, 0x4b0); /*config sunxi_mad_lpsd_rrun_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_LPSD_RRUN, 0x91); /*config sunxi_mad_lpsd_rstop_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_LPSD_RSTOP, 0xaa); /*config sunxi_mad_lpsd_ecnt_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_LPSD_ECNT, 0x32); } void sunxi_mad_sram_init(void) { /*config sunxi_mad_sram_point_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_POINT, 0x00); /*config sunxi_mad_sram_size_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_SIZE, MAD_SRAM_SIZE_VALUE); /*config sunxi_mad_sram_store_th_reg*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_STORE_TH, MAD_SRAM_STORE_TH_VALUE); /*config sunxi_mad_sram_sec_region_reg, non-sec*/ regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_SEC_REGION_REG, 0x0); /*config sunxi_mad_sram_size_reg*/ regmap_update_bits(sunxi_mad_regmap, SUNXI_MAD_CTRL, 1<src_maxburst; regmap_write(sunxi_mad_regmap, SUNXI_MAD_SRAM_AHB1_RX_TH, 0x40<dma_addr = MAD_SRAM_DMA_SRC_ADDR; capture_dma_param->dma_drq_type_num = DRQSRC_MAD_RX; /*Enable sram dma*/ /*regmap_update_bits(sunxi_mad_regmap, SUNXI_MAD_CTRL, 1<= 1) && (path_sel <= 5)) { regmap_update_bits(sunxi_mad_regmap, SUNXI_MAD_AD_PATH_SEL, MAD_AD_PATH_SEL_MASK, path_sel<dev.of_node; struct sunxi_mad_info *sunxi_mad; void __iomem *sunxi_mad_membase = NULL; unsigned int temp_val; int ret; sunxi_mad = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_mad_info), GFP_KERNEL); if (!sunxi_mad) { ret = -ENOMEM; goto err_node_put; } dev_set_drvdata(&pdev->dev, sunxi_mad); sunxi_mad->dev = &pdev->dev; ret = of_address_to_resource(np, 0, &res); if (ret) { dev_err(&pdev->dev, "Failed to get sunxi mad resource\n"); return -EINVAL; goto err_devm_kfree; } memregion = devm_request_mem_region(&pdev->dev, res.start, resource_size(&res), DRV_NAME); if (!memregion) { dev_err(&pdev->dev, "sunxi mad memory region already claimed\n"); ret = -EBUSY; goto err_devm_kfree; } sunxi_mad_membase = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); if (!sunxi_mad_membase) { dev_err(&pdev->dev, "sunxi mad ioremap failed\n"); ret = -EBUSY; goto err_devm_kfree; } sunxi_mad_regmap = devm_regmap_init_mmio(&pdev->dev, sunxi_mad_membase, &sunxi_mad_regmap_config); if (IS_ERR_OR_NULL(sunxi_mad_regmap)) { dev_err(&pdev->dev, "sunxi mad registers regmap failed\n"); ret = -ENOMEM; goto err_iounmap; } /* sunxi_mad->mad_cfg_clk = of_clk_get(np, 0); if (IS_ERR(sunxi_mad->mad_cfg_clk)) { dev_err(&pdev->dev, "Can't get mad cfg clocks\n"); ret = PTR_ERR(sunxi_mad->mad_cfg_clk); goto err_iounmap; } sunxi_mad->mad_ad_clk = of_clk_get(np, 1); if (IS_ERR(sunxi_mad->mad_ad_clk)) { dev_err(&pdev->dev, "Can't get mad ad clocks\n"); ret = PTR_ERR(sunxi_mad->mad_ad_clk); goto err_mad_cfg_clk_put; } */ sunxi_mad->mad_clk = of_clk_get(np, 0); if (IS_ERR(sunxi_mad->mad_clk)) { dev_err(&pdev->dev, "Can't get mad clocks\n"); ret = PTR_ERR(sunxi_mad->mad_clk); goto err_mad_ad_clk_put; } /* sunxi_mad->pll_clk = of_clk_get(np, 3); if (IS_ERR(sunxi_mad->pll_clk)) { dev_err(&pdev->dev, "Can't get pll clocks\n"); ret = PTR_ERR(sunxi_mad->pll_clk); goto err_mad_clk_put; } sunxi_mad->hosc_clk = of_clk_get(np, 4); if (IS_ERR(sunxi_mad->hosc_clk)) { dev_err(&pdev->dev, "Can't get 24MHz hosc clocks\n"); ret = PTR_ERR(sunxi_mad->hosc_clk); goto err_pll_clk_put; } */ sunxi_mad->lpsd_clk = of_clk_get(np, 1); if (IS_ERR(sunxi_mad->lpsd_clk)) { dev_err(&pdev->dev, "Can't get lpsd clocks\n"); ret = PTR_ERR(sunxi_mad->lpsd_clk); goto err_hosc_clk_put; } ret = of_property_read_u32(np, "lpsd_clk_src_cfg", &temp_val); if (ret <= 0) { pr_debug("lpsd clk source is pll_audio, 48 div!\n"); sunxi_mad->pll_audio_src_used = 1; } else { pr_debug("lpsd clk source is 24 MHz hosc!\n"); sunxi_mad->hosc_src_used = 1; } if (sunxi_mad->pll_audio_src_used == 1) { if (clk_set_parent(sunxi_mad->lpsd_clk, sunxi_mad->pll_clk)) { dev_err(&pdev->dev, "set parent of lpsd_clk to pll_clk fail\n"); ret = -EBUSY; goto err_lpsd_clk_put; } clk_prepare_enable(sunxi_mad->pll_clk); clk_prepare_enable(sunxi_mad->lpsd_clk); } else if (sunxi_mad->hosc_src_used == 1) { if (clk_set_parent(sunxi_mad->lpsd_clk, sunxi_mad->hosc_clk)) { dev_err(&pdev->dev, "set parent of lpsd_clk to hosc_clk fail\n"); ret = -EBUSY; goto err_lpsd_clk_put; } clk_prepare_enable(sunxi_mad->hosc_clk); clk_prepare_enable(sunxi_mad->lpsd_clk); } else { dev_err(&pdev->dev, "Can't set parent of lpsd_clk\n"); ret = -EBUSY; goto err_lpsd_clk_put; } /*clk_prepare_enable(sunxi_mad->mad_cfg_clk);*/ /*clk_prepare_enable(sunxi_mad->mad_ad_clk);*/ clk_prepare_enable(sunxi_mad->mad_clk); return 0; err_lpsd_clk_put: clk_put(sunxi_mad->lpsd_clk); err_hosc_clk_put: clk_put(sunxi_mad->hosc_clk); err_mad_ad_clk_put: clk_put(sunxi_mad->mad_ad_clk); err_iounmap: iounmap(sunxi_mad_membase); err_devm_kfree: devm_kfree(&pdev->dev, sunxi_mad); err_node_put: of_node_put(np); return ret; } static int __exit sunxi_mad_remove(struct platform_device *pdev) { struct sunxi_mad_info *sunxi_mad = dev_get_drvdata(&pdev->dev); /* clk_put(sunxi_mad->mad_cfg_clk); clk_put(sunxi_mad->mad_ad_clk); clk_put(sunxi_mad->mad_clk); clk_put(sunxi_mad->pll_clk); clk_put(sunxi_mad->hosc_clk); clk_put(sunxi_mad->lpsd_clk); */ devm_kfree(&pdev->dev, sunxi_mad); return 0; } static const struct dev_pm_ops sunxi_mad_pm_ops = { .suspend = sunxi_mad_suspend, .resume = sunxi_mad_resume, }; static const struct of_device_id sunxi_mad_of_match[] = { { .compatible = "allwinner,sunxi-mad", }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_mad_of_match); static struct platform_driver sunxi_mad_driver = { .probe = sunxi_mad_probe, .remove = __exit_p(sunxi_mad_remove), .driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = &sunxi_mad_pm_ops, .of_match_table = sunxi_mad_of_match, }, }; module_platform_driver(sunxi_mad_driver); MODULE_AUTHOR("wolfgang "); MODULE_DESCRIPTION("SUNXI MAD driver"); MODULE_ALIAS("platform:sunxi-mad"); MODULE_LICENSE("GPL");