Path: blob/master/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c
52116 views
// SPDX-License-Identifier: GPL-2.0+1/*2* Copyright 2025 NXP3*/45#include <linux/bitfield.h>6#include <linux/component.h>7#include <linux/module.h>8#include <linux/of_platform.h>9#include <linux/platform_device.h>10#include <linux/pm_runtime.h>11#include <linux/regmap.h>12#include <drm/bridge/dw_hdmi.h>13#include <sound/asoundef.h>1415#define HTX_PAI_CTRL 0x0016#define ENABLE BIT(0)1718#define HTX_PAI_CTRL_EXT 0x0419#define WTMK_HIGH_MASK GENMASK(31, 24)20#define WTMK_LOW_MASK GENMASK(23, 16)21#define NUM_CH_MASK GENMASK(10, 8)22#define WTMK_HIGH(n) FIELD_PREP(WTMK_HIGH_MASK, (n))23#define WTMK_LOW(n) FIELD_PREP(WTMK_LOW_MASK, (n))24#define NUM_CH(n) FIELD_PREP(NUM_CH_MASK, (n) - 1)2526#define HTX_PAI_FIELD_CTRL 0x0827#define PRE_SEL GENMASK(28, 24)28#define D_SEL GENMASK(23, 20)29#define V_SEL GENMASK(19, 15)30#define U_SEL GENMASK(14, 10)31#define C_SEL GENMASK(9, 5)32#define P_SEL GENMASK(4, 0)3334struct imx8mp_hdmi_pai {35struct regmap *regmap;36struct device *dev;37};3839static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel,40int width, int rate, int non_pcm,41int iec958)42{43const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);44struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;45int val;4647if (pm_runtime_resume_and_get(hdmi_pai->dev) < 0)48return;4950/* PAI set control extended */51val = WTMK_HIGH(3) | WTMK_LOW(3);52val |= NUM_CH(channel);53regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val);5455/* IEC60958 format */56if (iec958) {57val = FIELD_PREP_CONST(P_SEL,58__bf_shf(IEC958_SUBFRAME_PARITY));59val |= FIELD_PREP_CONST(C_SEL,60__bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS));61val |= FIELD_PREP_CONST(U_SEL,62__bf_shf(IEC958_SUBFRAME_USER_DATA));63val |= FIELD_PREP_CONST(V_SEL,64__bf_shf(IEC958_SUBFRAME_VALIDITY));65val |= FIELD_PREP_CONST(D_SEL,66__bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK));67val |= FIELD_PREP_CONST(PRE_SEL,68__bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK));69} else {70/*71* The allowed PCM widths are 24bit and 32bit, as they are supported72* by aud2htx module.73* for 24bit, D_SEL = 0, select all the bits.74* for 32bit, D_SEL = 8, select 24bit in MSB.75*/76val = FIELD_PREP(D_SEL, width - 24);77}7879regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val);8081/* PAI start running */82regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE);83}8485static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi)86{87const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);88struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;8990/* Stop PAI */91regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0);9293pm_runtime_put_sync(hdmi_pai->dev);94}9596static const struct regmap_config imx8mp_hdmi_pai_regmap_config = {97.reg_bits = 32,98.reg_stride = 4,99.val_bits = 32,100.max_register = HTX_PAI_FIELD_CTRL,101};102103static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, void *data)104{105struct platform_device *pdev = to_platform_device(dev);106struct dw_hdmi_plat_data *plat_data = data;107struct imx8mp_hdmi_pai *hdmi_pai;108struct resource *res;109void __iomem *base;110int ret;111112hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL);113if (!hdmi_pai)114return -ENOMEM;115116base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);117if (IS_ERR(base))118return PTR_ERR(base);119120hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base,121&imx8mp_hdmi_pai_regmap_config);122if (IS_ERR(hdmi_pai->regmap)) {123dev_err(dev, "regmap init failed\n");124return PTR_ERR(hdmi_pai->regmap);125}126127plat_data->enable_audio = imx8mp_hdmi_pai_enable;128plat_data->disable_audio = imx8mp_hdmi_pai_disable;129plat_data->priv_audio = hdmi_pai;130131hdmi_pai->dev = dev;132ret = devm_pm_runtime_enable(dev);133if (ret < 0) {134dev_err(dev, "failed to enable PM runtime: %d\n", ret);135return ret;136}137138return 0;139}140141static const struct component_ops imx8mp_hdmi_pai_ops = {142.bind = imx8mp_hdmi_pai_bind,143};144145static int imx8mp_hdmi_pai_probe(struct platform_device *pdev)146{147return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops);148}149150static void imx8mp_hdmi_pai_remove(struct platform_device *pdev)151{152component_del(&pdev->dev, &imx8mp_hdmi_pai_ops);153}154155static const struct of_device_id imx8mp_hdmi_pai_of_table[] = {156{ .compatible = "fsl,imx8mp-hdmi-pai" },157{ /* Sentinel */ }158};159MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table);160161static struct platform_driver imx8mp_hdmi_pai_platform_driver = {162.probe = imx8mp_hdmi_pai_probe,163.remove = imx8mp_hdmi_pai_remove,164.driver = {165.name = "imx8mp-hdmi-pai",166.of_match_table = imx8mp_hdmi_pai_of_table,167},168};169module_platform_driver(imx8mp_hdmi_pai_platform_driver);170171MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver");172MODULE_LICENSE("GPL");173174175