Загрузка данных


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");