Загрузка данных
static int fsl_xcvr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fsl_xcvr *xcvr;
struct resource *rx_res, *tx_res;
void __iomem *regs;
struct device_node *hdmi_np;
int ret, irq;
int i, j, k = 0;
u64 clk_rate[2];
xcvr = devm_kzalloc(dev, sizeof(*xcvr), GFP_KERNEL);
if (!xcvr)
return -ENOMEM;
xcvr->pdev = pdev;
xcvr->soc_data = of_device_get_match_data(&pdev->dev);
xcvr->ipg_clk = devm_clk_get(dev, "ipg");
if (IS_ERR(xcvr->ipg_clk)) {
dev_err(dev, "failed to get ipg clock\n");
return PTR_ERR(xcvr->ipg_clk);
}
xcvr->phy_clk = devm_clk_get(dev, "phy");
if (IS_ERR(xcvr->phy_clk)) {
dev_err(dev, "failed to get phy clock\n");
return PTR_ERR(xcvr->phy_clk);
}
xcvr->spba_clk = devm_clk_get(dev, "spba");
if (IS_ERR(xcvr->spba_clk)) {
dev_err(dev, "failed to get spba clock\n");
return PTR_ERR(xcvr->spba_clk);
}
xcvr->pll_ipg_clk = devm_clk_get(dev, "pll_ipg");
if (IS_ERR(xcvr->pll_ipg_clk)) {
dev_err(dev, "failed to get pll_ipg clock\n");
return PTR_ERR(xcvr->pll_ipg_clk);
}
fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk,
&xcvr->pll11k_clk);
xcvr->spdif_constr_rates = fsl_xcvr_spdif_rates_constr;
if (xcvr->soc_data->spdif_only) {
xcvr->spdif_constr_rates.list = xcvr->spdif_constr_rates_list;
xcvr->spdif_constr_rates.count = 0;
for (i = 0; i < SPDIF_NUM_RATES; i++) {
clk_rate[0] = clk_get_rate(xcvr->pll8k_clk);
clk_rate[1] = clk_get_rate(xcvr->pll11k_clk);
if (!(clk_rate[0] || clk_rate[1]))
clk_rate[0] = clk_get_rate(xcvr->phy_clk);
for (j = 0; j < 2; j++) {
if (clk_rate[j] != 0 &&
do_div(clk_rate[j], fsl_xcvr_spdif_rates[i]) == 0) {
xcvr->spdif_constr_rates_list[k++] =
fsl_xcvr_spdif_rates[i];
xcvr->spdif_constr_rates.count++;
}
}
}
}
xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram");
if (IS_ERR(xcvr->ram_addr))
return PTR_ERR(xcvr->ram_addr);
regs = devm_platform_ioremap_resource_byname(pdev, "regs");
if (IS_ERR(regs))
return PTR_ERR(regs);
xcvr->regmap = devm_regmap_init_mmio_clk(dev, NULL, regs,
&fsl_xcvr_regmap_cfg);
if (IS_ERR(xcvr->regmap)) {
dev_err(dev, "failed to init XCVR regmap: %ld\n",
PTR_ERR(xcvr->regmap));
return PTR_ERR(xcvr->regmap);
}
if (xcvr->soc_data->use_phy) {
xcvr->regmap_phy = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_phy_cfg);
if (IS_ERR(xcvr->regmap_phy)) {
dev_err(dev, "failed to init XCVR PHY regmap: %ld\n",
PTR_ERR(xcvr->regmap_phy));
return PTR_ERR(xcvr->regmap_phy);
}
switch (xcvr->soc_data->pll_ver) {
case PLL_MX8MP:
xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_pllv0_cfg);
if (IS_ERR(xcvr->regmap_pll)) {
dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
PTR_ERR(xcvr->regmap_pll));
return PTR_ERR(xcvr->regmap_pll);
}
break;
case PLL_MX95:
xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_pllv1_cfg);
if (IS_ERR(xcvr->regmap_pll)) {
dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
PTR_ERR(xcvr->regmap_pll));
return PTR_ERR(xcvr->regmap_pll);
}
break;
default:
dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
return -EINVAL;
}
}
xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(xcvr->reset)) {
dev_err(dev, "failed to get XCVR reset control\n");
return PTR_ERR(xcvr->reset);
}
/* get IRQs */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, irq0_isr, 0, pdev->name, xcvr);
if (ret) {
dev_err(dev, "failed to claim IRQ0: %i\n", ret);
return ret;
}
rx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxfifo");
tx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txfifo");
if (!rx_res || !tx_res) {
dev_err(dev, "could not find rxfifo or txfifo resource\n");
return -EINVAL;
}
xcvr->dma_prms_rx.chan_name = "rx";
xcvr->dma_prms_tx.chan_name = "tx";
xcvr->dma_prms_rx.addr = rx_res->start;
xcvr->dma_prms_tx.addr = tx_res->start;
xcvr->dma_prms_rx.maxburst = FSL_XCVR_MAXBURST_RX;
xcvr->dma_prms_tx.maxburst = FSL_XCVR_MAXBURST_TX;
platform_set_drvdata(pdev, xcvr);
pm_runtime_enable(dev);
regcache_cache_only(xcvr->regmap, true);
if (xcvr->soc_data->use_phy) {
regcache_cache_only(xcvr->regmap_phy, true);
regcache_cache_only(xcvr->regmap_pll, true);
}
/*
* Register platform component before registering cpu dai for there
* is not defer probe for platform component in snd_soc_add_pcm_runtime().
*/
ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
if (ret) {
pm_runtime_disable(dev);
dev_err(dev, "failed to pcm register\n");
return ret;
}
ret = devm_snd_soc_register_component(dev, &fsl_xcvr_comp,
&fsl_xcvr_dai, 1);
if (ret) {
pm_runtime_disable(dev);
dev_err(dev, "failed to register component %s\n",
fsl_xcvr_comp.name);
return ret;
}
INIT_WORK(&xcvr->work_rst, reset_rx_work);
spin_lock_init(&xcvr->lock);
INIT_WORK(&xcvr->work, edid_work);
hdmi_np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0);
if (hdmi_np)
xcvr->bridge = of_drm_find_bridge(hdmi_np);
ret = sysfs_create_group(&pdev->dev.kobj, fsl_xcvr_get_attr_grp());
if (ret) {
pm_runtime_disable(dev);
dev_err(&pdev->dev, "fail to create sys group\n");
}
return ret;
}
static void fsl_xcvr_remove(struct platform_device *pdev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
cancel_work_sync(&xcvr->work_rst);
cancel_work_sync(&xcvr->work);
sysfs_remove_group(&pdev->dev.kobj, fsl_xcvr_get_attr_grp());
pm_runtime_disable(&pdev->dev);
}
static int fsl_xcvr_runtime_suspend(struct device *dev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
int ret;
if (!xcvr->soc_data->spdif_only &&
xcvr->mode == FSL_XCVR_MODE_EARC) {
/* Assert M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET,
FSL_XCVR_EXT_CTRL_CORE_RESET);
if (ret < 0)
dev_err(dev, "Failed to assert M0+ core: %d\n", ret);
}
regcache_cache_only(xcvr->regmap, true);
if (xcvr->soc_data->use_phy) {
regcache_cache_only(xcvr->regmap_phy, true);
regcache_cache_only(xcvr->regmap_pll, true);
}
clk_disable_unprepare(xcvr->spba_clk);
clk_disable_unprepare(xcvr->phy_clk);
clk_disable_unprepare(xcvr->pll_ipg_clk);
clk_disable_unprepare(xcvr->ipg_clk);
return 0;
}
static int fsl_xcvr_runtime_resume(struct device *dev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
int ret;
ret = reset_control_assert(xcvr->reset);
if (ret < 0) {
dev_err(dev, "Failed to assert M0+ reset: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(xcvr->ipg_clk);
if (ret) {
dev_err(dev, "failed to start IPG clock.\n");
return ret;
}
ret = clk_prepare_enable(xcvr->pll_ipg_clk);
if (ret) {
dev_err(dev, "failed to start PLL IPG clock.\n");
goto stop_ipg_clk;
}
ret = clk_prepare_enable(xcvr->phy_clk);
if (ret) {
dev_err(dev, "failed to start PHY clock: %d\n", ret);
goto stop_pll_ipg_clk;
}
ret = clk_prepare_enable(xcvr->spba_clk);
if (ret) {
dev_err(dev, "failed to start SPBA clock.\n");
goto stop_phy_clk;
}
ret = reset_control_deassert(xcvr->reset);
if (ret) {
dev_err(dev, "failed to deassert M0+ reset.\n");
goto stop_spba_clk;
}
regcache_cache_only(xcvr->regmap, false);
regcache_mark_dirty(xcvr->regmap);
ret = regcache_sync(xcvr->regmap);
if (ret) {
dev_err(dev, "failed to sync regcache.\n");
goto stop_spba_clk;
}
if (xcvr->soc_data->use_phy) {
ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
if (ret < 0) {
dev_err(dev, "Error while release PHY reset: %d\n", ret);
goto stop_spba_clk;
}
regcache_cache_only(xcvr->regmap_phy, false);
regcache_mark_dirty(xcvr->regmap_phy);
ret = regcache_sync(xcvr->regmap_phy);
if (ret) {
dev_err(dev, "failed to sync phy regcache.\n");
goto stop_spba_clk;
}
regcache_cache_only(xcvr->regmap_pll, false);
regcache_mark_dirty(xcvr->regmap_pll);
ret = regcache_sync(xcvr->regmap_pll);
if (ret) {
dev_err(dev, "failed to sync pll regcache.\n");
goto stop_spba_clk;
}
}
if (xcvr->soc_data->fw_name) {
ret = fsl_xcvr_load_firmware(xcvr);
if (ret) {
dev_err(dev, "failed to load firmware.\n");
goto stop_spba_clk;
}
/* Release M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
if (ret < 0) {
dev_err(dev, "M0+ core release failed: %d\n", ret);
goto stop_spba_clk;
}
/* Let M0+ core complete firmware initialization */
msleep(50);
}
return 0;
stop_spba_clk:
clk_disable_unprepare(xcvr->spba_clk);
stop_phy_clk:
clk_disable_unprepare(xcvr->phy_clk);
stop_pll_ipg_clk:
clk_disable_unprepare(xcvr->pll_ipg_clk);
stop_ipg_clk:
clk_disable_unprepare(xcvr->ipg_clk);
return ret;
}
static const struct dev_pm_ops fsl_xcvr_pm_ops = {
RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver fsl_xcvr_driver = {
.probe = fsl_xcvr_probe,
.driver = {
.name = "fsl,imx8mp-audio-xcvr",
.pm = pm_ptr(&fsl_xcvr_pm_ops),
.of_match_table = fsl_xcvr_dt_ids,
},
.remove = fsl_xcvr_remove,
};
module_platform_driver(fsl_xcvr_driver);
MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
MODULE_DESCRIPTION("NXP Audio Transceiver (XCVR) driver");
MODULE_LICENSE("GPL v2");